-1

我最近发现了关于Threads在我的应用程序中使用AutoResetEvent. 在控制台应用程序中,我会创建一个while(true)循环并依靠AutoResetEvent.WaitOne()等待线程发出信号,例如数据已准备好使用AutoResetEvent.Set(). 我知道如何使用Class具有工作线程方法的线程之间共享数据,以及在工作线程和主线程之间共享的数据元素。

我的问题是如何AutoResetEvent在 Winforms 应用程序中使用,我通常看不到命令​​循环。我应该将WaitOne()呼叫放在 Winforms 应用程序的哪个位置?

关于示例代码:最好的示例是本网站上关于如何使用超时制作 a的答案Console.ReadLine(),这基本上是一个关于如何使用signals. 该示例是控制台应用程序示例。

谷歌搜索时,我的问题的答案可能是使用BackgroundWorker控件?

4

3 回答 3

1

是的,BackgroundWorker改为使用。

于 2015-03-11T15:47:03.593 回答
1

似乎在 Winforms 中使用多线程的完整解决方案是结合BackgroundWorkerAutoResetEvent.

如果我是正确的,那么BackgroundWorker就像一个Thread,其优点是它与ProgressChanged事件处理程序很好地接口,用于从后台线程向 UI 发出信号。我们可能仍然需要AutoResetEvent从 UI 到后台线程的信号。就我而言,我想使用 Next 按钮启动一个新的阻塞调用来接收数据。下面是我刚刚测试的代码。

然而,问题是,如果真的没有比AutoResetEvent在这里使用更好的方法。不过,似乎工作得很好。

/// <summary>
/// The Next button uses this to signal the BackgroundWorker
/// to start the blocking call to Receive data
/// </summary>
private AutoResetEvent _SignalStartReceive  = new AutoResetEvent(false);

/// <summary>
/// To implement variable time it takes until Receive returns
/// </summary>
private Random _RandomTime = new Random();

// Class Initializer
public Form()
{
    backgroundWorker_Receive.WorkerReportsProgress = true;
    backgroundWorker_Receive.RunWorkerAsync();
    return;
}

/// <summary>
/// User presses this button when he is ready to Receive the next
/// data packet
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_ReceiveNext_Click(object sender, EventArgs e)
{
    checkBox_Receive.Checked = true;
    textBox_ReceivedContent.Text = "";
    _SignalStartReceive.Set();
    return;
}

/// <summary>
/// User presses this button when he is ready to Receive the next
/// data packet
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_ReceiveNext_Click(object sender, EventArgs e)
{
    checkBox_Receive.Checked = true;
    textBox_ReceivedContent.Text = "";
    _SignalStartReceive.Set();
    return;
}

/// <summary>
/// This is the worker thread, running in the background
/// while the UI stays responsive
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void backgroundWorker_Receive_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = sender as BackgroundWorker;
    while (true)
    {
        // blocking: wait for button click
        _SignalStartReceive.WaitOne();
        // blocking: wait for datagram over network
#if true //temporary code to simulate UdpClient.Receive()
        DateTime StartTime = DateTime.Now;
        int RandomTimeMs = 2000 + 30 * _RandomTime.Next(100);
        Thread.Sleep(RandomTimeMs);
        _ReceivedDatagram = string.Format("UDP data ... {0} ms", (DateTime.Now - StartTime).TotalMilliseconds);
#else
        something with UdpClient.Receive();
#endif
        // succeeded:
        worker.ReportProgress(0);//fire the event: Receive_ProgressChanged (argument does not matter)
    }
    //return; //unreachable, but would fire the Completed event
}

/// <summary>
/// Handler for the ReportProgress() call by the BackgroundWorker Thread
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void backgroundWorker_Receive_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    textBox_ReceivedContent.Text = _ReceivedDatagram;
    checkBox_Receive.Checked = false;
    return;
}
于 2015-03-12T16:39:48.693 回答
1

根据您的问题,我了解到您的 UI 线程应该等待一个事件。

WinForms 应用程序的 UI 线程必须将控制权传递给操作系统以允许用户交互,因此等待 anAutoResetEvent是不合适的。如果要与 UI 线程通信,可以使用:

bool ret = (bool)myFormInstance.Invoke(
    new Func<DataClass, bool>(myFormInstance.DoStuff), 
    myData);

public class MyForm : Form
{
    public bool DoStuff(DataClass data)
    {
        ....
    }
}

您还可以使用BeginInvoke在 UI 线程上异步运行代码。

如果您想要相反的事情(UI 线程告诉工作线程开始处理),您可以使用BackgroundWorker并且应该有很多关于它的教程。

于 2015-03-11T15:45:17.697 回答