Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/285.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
两个窗体之间的线程安全窗体操作(WinForms C#)_C#_Winforms - Fatal编程技术网

两个窗体之间的线程安全窗体操作(WinForms C#)

两个窗体之间的线程安全窗体操作(WinForms C#),c#,winforms,C#,Winforms,我有两个窗体,一个是主窗体,另一个是弹出的模态对话框。从主窗体中生成的进程中,我希望动态更新模式对话框上的文本。以下是我所拥有的: 在主窗体中,我执行以下操作: // show the wait modal var modal = new WaitDialog { Owner = this }; // thread the packaging var thread = new Thread(() => Package

我有两个窗体,一个是主窗体,另一个是弹出的模态对话框。从主窗体中生成的进程中,我希望动态更新模式对话框上的文本。以下是我所拥有的:

在主窗体中,我执行以下操作:

// show the wait modal
            var modal = new WaitDialog { Owner = this };

            // thread the packaging
            var thread = new Thread(() => Packager.PackageUpdates(clients, version, modal));
            thread.Start();

            // hopefully it worked ...
            if (modal.ShowDialog() != DialogResult.OK)
            {
                throw new Exception("Something failed, miserably.");
            }
PackageUpdates
方法获取模式对话框,并执行以下操作:

 // quick update and sleep for a sec ...
             modal.SetWaitLabelText("Downloading update package...");
             Thread.Sleep(2000);

             modal.SetWaitLabelText("Re-packaging update...");
为了线程安全,我在模式对话框中执行以下操作:

public void SetWaitLabelText(string text)
        {
            if (lblWaitMessage.InvokeRequired)
            {
                Invoke(new Action<string>(SetWaitLabelText), text);
            }
            else
            {
                lblWaitMessage.Text = text;
            }
        }
public void SetWaitLabelText(字符串文本)
{
if(lblWaitMessage.invokererequired)
{
调用(新操作(SetWaitLabelText),文本);
}
其他的
{
lblWaitMessage.Text=文本;
}
}
一切都很好。。。大多数时候。每弹出三到四次模态,我就会在
lblWaitMessage.Text=Text上得到一个异常并且它没有调用命令


此设置中是否缺少某些内容?

我认为您应该重写代码,以便在modal.ShowDialog()之前不调用thread.Start()

作为解决方法,您可以尝试以下方法:

public void SetWaitLabelText(string text) {
    Invoke(new Action<string>(SetWaitLabelText2), text);
}

void SetWaitLabelText2(string text) {
    lblWaitMessage.Text = text;
}
public void SetWaitLabelText(字符串文本){
调用(新操作(SetWaitLabelText2),文本);
}
void SetWaitLabelText2(字符串文本){
lblWaitMessage.Text=文本;
}

第一个方法始终使用Invoke,而不考虑invokererequired的值。第二种方法实际上起作用。当您总是从另一个线程调用函数时,此模式可用。

就像@Hans Passant指出的那样,您应该等待modal.Load-event。一个好的选择是使用通知线程等待,直到发生这种情况

WaitOne方法将阻塞线程,直到调用Set方法为止。这里有一个非常简单的设置,应该可以实现这一点

public partial class Form1 : Form
{
    ManualResetEvent m_ResetEvent;

    public Form1()
    {
        InitializeComponent();

        m_ResetEvent = new ManualResetEvent(false);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        Dialog d = new Dialog { Owner = this, ResetEvent = m_ResetEvent };

        var thread = new Thread(new ParameterizedThreadStart(DoSomething));
        thread.Start(d);

        if (d.ShowDialog() != System.Windows.Forms.DialogResult.OK)
        {
            throw new Exception("Something terrible happened");
        }

    }

    private void DoSomething(object modal)
    {
        Dialog d = (Dialog)modal;     

        // Block the thread!
        m_ResetEvent.WaitOne();

        for (int i = 0; i < 1000; i++)
        {
            d.SetWaitLabelText(i.ToString());
            Thread.Sleep(1000);
        }
    }
}
公共部分类表单1:表单
{
手动复位事件m_复位事件;
公共表格1()
{
初始化组件();
m_ResetEvent=新手动复位事件(错误);
}
私有无效按钮1\u单击(对象发送者,事件参数e)
{
对话框d=新建对话框{Owner=this,ResetEvent=m_ResetEvent};
var thread=新线程(新的参数化线程启动(DoSomething));
线程启动(d);
如果(d.ShowDialog()!=System.Windows.Forms.DialogResult.OK)
{
抛出新异常(“发生了可怕的事情”);
}
}
私有void DoSomething(对象模式)
{
对话框d=(对话框)模态;
//挡住线!
m_ResetEvent.WaitOne();
对于(int i=0;i<1000;i++)
{
d、 SetWaitLabelText(i.ToString());
睡眠(1000);
}
}
}
这是模态形式

public partial class Dialog : Form
{
    public Form Owner { get; set; }

    public ManualResetEvent ResetEvent { get; set; }

    public Dialog()
    {
        InitializeComponent();
    }

    public void SetWaitLabelText(string text)
    {
        if (label1.InvokeRequired)
        {
            Invoke(new Action<string>(SetWaitLabelText), text);
        }
        else
        {
            label1.Text = text;
        }
    }

    private void Dialog_Load(object sender, EventArgs e)
    {
        // Set the event, thus unblocking the other thread
        ResetEvent.Set();
    }
}
public分部类对话框:表单
{
公共表单所有者{get;set;}
public ManualResetEvent ResetEvent{get;set;}
公共对话()
{
初始化组件();
}
公共void SetWaitLabelText(字符串文本)
{
if(标签1.InvokeRequired)
{
调用(新操作(SetWaitLabelText),文本);
}
其他的
{
标签1.Text=文本;
}
}
私有无效对话框\u加载(对象发送方、事件参数)
{
//设置事件,从而取消阻止另一个线程
ResetEvent.Set();
}
}

您启动线程太快。等待modal.Load事件。正如Hans Passant所说,我不确定在modal.ShowDialog()之前是否可以安全地使用thread.Start()。什么是例外?你得到的是什么样的例外。正如Hans所说,很可能在表单完全构建之前,线程正在执行对
SetWaitLabelText
的第一次调用。自从我发布此消息后,我再也没有收到异常。我将继续尝试并发布异常情况,并尝试Hans的解决方案。这是一个肮脏的解决方案…:)