如何在 WinForms 中使用 AutoResetEvent
How to use AutoResetEvent in WinForms
我最近发现在我的应用程序中使用 AutoResetEvent
在 Threads
之间进行消息传递。在控制台应用程序中,我会创建一个 while(true)
循环并依赖 AutoResetEvent.WaitOne()
来等待线程发出信号,例如数据已准备好使用 AutoResetEvent.Set()
进行处理。我知道如何使用具有工作线程方法的 Class
在线程之间共享数据,以及如何在工作线程和主线程之间共享数据元素。
我的问题是如何在我通常看不到命令循环的 Winforms 应用程序中使用 AutoResetEvent
。我应该将 WaitOne()
调用放在 Winforms 应用程序的什么位置?
关于示例代码:最好的示例是 answer on this site 关于如何使用超时创建 Console.ReadLine()
的示例,这基本上是关于如何使用 signals
的简单示例。该示例是控制台应用程序示例。
谷歌搜索时,我的问题的答案可能是使用 BackgroundWorker
控件 ?
根据您的问题,我了解到您的 UI 线程应该等待一个事件。
WinForms 应用程序的 UI 线程必须将控制权传递给操作系统以允许用户交互,因此等待 AutoResetEvent
是不合适的。如果要与UI线程通信,可以使用:
bool ret = (bool)myFormInstance.Invoke(
new Func<DataClass, bool>(myFormInstance.DoStuff),
myData);
public class MyForm : Form
{
public bool DoStuff(DataClass data)
{
....
}
}
您还可以在 UI 线程上异步使用 BeginInvoke
到 运行 代码。
如果你想要反过来(UI 线程告诉工作线程开始处理)你可以使用 BackgroundWorker
并且应该有很多关于它的教程。
是,请改用 BackgroundWorker
。
似乎在 Winforms 中使用多线程的完整解决方案是结合 BackgroundWorker
和 AutoResetEvent
。
如果我是正确的,那么 BackgroundWorker
就像一个 Thread
,其优点是它与 ProgressChanged
事件处理程序很好地交互,用于从后台线程向 [=24] 发出信号=].我们可能仍然需要 AutoResetEvent
用于从 UI 到后台线程的信号。就我而言,我想使用“下一步”按钮启动新的阻塞调用以接收数据。下面是我刚刚测试的代码。
但是,问题是,如果真的没有比在这里使用 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;
}
我最近发现在我的应用程序中使用 AutoResetEvent
在 Threads
之间进行消息传递。在控制台应用程序中,我会创建一个 while(true)
循环并依赖 AutoResetEvent.WaitOne()
来等待线程发出信号,例如数据已准备好使用 AutoResetEvent.Set()
进行处理。我知道如何使用具有工作线程方法的 Class
在线程之间共享数据,以及如何在工作线程和主线程之间共享数据元素。
我的问题是如何在我通常看不到命令循环的 Winforms 应用程序中使用 AutoResetEvent
。我应该将 WaitOne()
调用放在 Winforms 应用程序的什么位置?
关于示例代码:最好的示例是 answer on this site 关于如何使用超时创建 Console.ReadLine()
的示例,这基本上是关于如何使用 signals
的简单示例。该示例是控制台应用程序示例。
谷歌搜索时,我的问题的答案可能是使用 BackgroundWorker
控件 ?
根据您的问题,我了解到您的 UI 线程应该等待一个事件。
WinForms 应用程序的 UI 线程必须将控制权传递给操作系统以允许用户交互,因此等待 AutoResetEvent
是不合适的。如果要与UI线程通信,可以使用:
bool ret = (bool)myFormInstance.Invoke(
new Func<DataClass, bool>(myFormInstance.DoStuff),
myData);
public class MyForm : Form
{
public bool DoStuff(DataClass data)
{
....
}
}
您还可以在 UI 线程上异步使用 BeginInvoke
到 运行 代码。
如果你想要反过来(UI 线程告诉工作线程开始处理)你可以使用 BackgroundWorker
并且应该有很多关于它的教程。
是,请改用 BackgroundWorker
。
似乎在 Winforms 中使用多线程的完整解决方案是结合 BackgroundWorker
和 AutoResetEvent
。
如果我是正确的,那么 BackgroundWorker
就像一个 Thread
,其优点是它与 ProgressChanged
事件处理程序很好地交互,用于从后台线程向 [=24] 发出信号=].我们可能仍然需要 AutoResetEvent
用于从 UI 到后台线程的信号。就我而言,我想使用“下一步”按钮启动新的阻塞调用以接收数据。下面是我刚刚测试的代码。
但是,问题是,如果真的没有比在这里使用 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;
}