wpf c# backgroundworker 等到完成
wpf c# backgroundworker wait until finished
我的 wpf 应用程序中有几个文本框。每个文本框的 LostFocus-Event 都会启动后台程序将数据发送到连接的串行端口。
private readonly BackgroundWorker online_mode_send_worker = new BackgroundWorker();
online_mode_send_worker.DoWork += online_mode_send_worker_DoWork;
online_mode_send_worker.RunWorkerCompleted += online_mode_send_worker_RunWorkerCompleted;
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
online_mode_send_worker.RunWorkerAsync(data);
}
private void online_mode_send_worker_DoWork(object sender, DoWorkEventArgs e)
{
List<object> data = (List<object>)e.Argument;
Port.WriteLine(STARTCHARACTER + XMLSET + XML_TAG_START + data[0] + XML_TAG_STOP + data[1] + ENDCHARACTER);
string received = Port.ReadLine();
}
private void online_mode_send_worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//do some things after worker completed
}
至此,一切正常。
但有时我必须一个接一个地发送两个数据点,这时我遇到了问题。
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
online_mode_send_worker.RunWorkerAsync(data1);
//wait until backgroundworker has finished
online_mode_send_worker.RunWorkerAsync(data2);
}
Backgroundworker
仍然是 运行,我抛出了一个异常。
是否可以等到第一个 online_mode_send_worker.RunWorkerAsync(data)
完成后再开始第二个 online_mode_send_worker.RunWorkerAsync(data)
?
while(online_mode_send_worker.isBusy);
不工作,因为主线程正在阻塞并且 RunWorkerCompleted()
没有被抛出,所以 Backgroundwoker
总是很忙。
我找到了类似的东西,但是 Application.DoEvents()
在 wpf 中不可用。
while (online_mode_send_worker.IsBusy)
{
Application.DoEvents();
System.Threading.Thread.Sleep(100);
}
看来我错过了连载的东西。所以你要做的是同步你的异步调用:
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Run(() => mySerialDevice1.WriteData(data1));
Task.Run(() => mySerialDevice1.WriteData(data2));
}
public class SerialDevice
{
public Port Port { get; set; }
public object _LockWriteData = new object();
public void WriteData(string data)
{
lock(_LockWriteData)
{
Port.WriteLine(data);
}
}
}
另见:
- https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx
- https://msdn.microsoft.com/en-us/library/de0542zz(v=vs.110).aspx
原始答案
您可以使用 Task
而不是 Backgroundworker。
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Run(() => OnlineModeSendData(data1));
Task.Run(() => OnlineModeSendData(data2));
}
private void OnlineModeSendData(List<string> data)
{
Port.WriteLine(STARTCHARACTER + XMLSET + XML_TAG_START + data[0]+ XML_TAG_STOP + data[1] + ENDCHARACTER);
string received = Port.ReadLine();
}
我还想建议您制作真实的对象,而不是将字符串数组作为参数传递。
例如发送 BlinkLedRequest:
public class BlinkLedRequest
{
public int LedId{get;set;}
public int DurationInMilliseconds {get;set}
}
和相应的方法:
public void SendBlinkLed(BlickLedRequest request)
{
....
}
这是我在评论中提到的粗略想法。
public class Messenger {
private readonly BackgroundWorker online_mode_send_worker = new BackgroundWorker();
private readonly ConcurrentQueue<object> messages;
public Messenger() {
messages = new ConcurrentQueue<object>();
online_mode_send_worker.DoWork += online_mode_send_worker_DoWork;
online_mode_send_worker.RunWorkerCompleted += online_mode_send_worker_RunWorkerCompleted;
}
public void SendAsync(object message) {
if (online_mode_send_worker.IsBusy) {
messages.Enqueue(message);
} else {
online_mode_send_worker.RunWorkerAsync(message);
}
}
public Action<object> MessageHandler = delegate { };
private void online_mode_send_worker_DoWork(object sender, DoWorkEventArgs e) {
if (MessageHandler != null)
MessageHandler(e.Argument);
}
private void online_mode_send_worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
object nextMessage = null;
if (messages.Count > 0 && messages.TryDequeue(out nextMessage)) {
online_mode_send_worker.RunWorkerAsync(nextMessage);
}
}
}
您有一个队列来保留在后台工作人员忙碌时发送的消息,并让工作人员在完成工作后检查队列中是否有任何待处理的消息。
信使可以这样用
private Messenger messenger = new Messenger();
private void Initialize() { //I would expect this to be in the constructor
messenger.MessageHandler = MessageHandler;
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
messenger.SendAsync(data);
}
private void MessageHandler(object message)
{
List<object> data = (List<object>)message;
Port.WriteLine(STARTCHARACTER + XMLSET + XML_TAG_START + data[0] + XML_TAG_STOP + data[1] + ENDCHARACTER);
string received = Port.ReadLine();
}
我认为你应该使用 RunWorkerCompleted 事件并添加一个委托:
online_mode_send_worker.RunWorkerCompleted += (s, ev) =>
{
if (ev.Error != null)
{
//log Exception
}
//if(conditionToBrake)
// return;
online_mode_send_worker.RunWorkerAsync(data2);
};
online_mode_send_worker.RunWorkerCompleted(data1);
确保你设置了条件以避免无限循环。
更新:
bw.RunWorkerAsync(data1);
//wait here
bw.RunWorkerAsync(data2);
不是一个好的方法,因为UI会在等待的时候被阻塞。更好:
bw.RunWorkerAsync(new object[] { data1, data2 }); //or new object[] { data1 } if no data2
原回答:
我建议不要使用构造:while (bw.Busy) { ... }
(它会消耗cpu时间),使用同步对象,例如ManualResetEvent
BackgroundWorker 很棒class,但不支持等待。只需创建等待的添加对象:
var bw = new BackgroundWorker();
bw.DoWork += Bw_DoWork;
bw.RunWorkerCompleted += Bw_RunWorkerCompleted;
bool wasError;
ManualResetEvent e = null;
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
if (e != null)
return;
wasError = false;
e = new ManualResetEvent(false); //not signaled
bw.RunWorkerAsync(data1);
e.Wait(); //much better than while(bw.Busy())
if (!wasError)
bw.RunWorkerAsync(data2);
e = null;
}
private void Bw_DoWork(object sender, DoWorkEventArgs e)
{
//background work in another thread
}
private void Bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
//catch exception here
wasError = true;
}
e.Set(); //switch to signaled
}
我想说的是,如果您必须等到第一个 "job" 完成之后,您想要的是 Task.ContinueWith()
并相应地更改您的界面。 IMO 的 msdn page is good,但请注意您正在等待 "correct" 任务对象。提示:您应该在 Wait()
上调用 ContinueWith()
的 return 值。这是启动 Task
然后等待它的好模式,只要你可以保留 returned 的 Task
,这样你就可以等待它。
对于更通用的 "I only want one background thread doing things in the order they're added, and I want to wait until they're ALL done and I know when I'm done adding.",我建议使用只有一个线程使用它们的 BlockingCollection<Action>
。 in this other answer.
提供了如何执行此操作的示例
如果您只需要调用两次,您可以这样做:
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
online_mode_send_worker.RunWorkerAsync(data2);
}
但是,如果您需要对命令进行排队,则需要使用任务以另一种方式重写。
一个任务,在其中你将有一个 for 循环,你将在其中通过串行端口顺序发送数据。
https://msdn.microsoft.com/pt-br/library/system.threading.tasks.task(v=vs.110).aspx
我的 wpf 应用程序中有几个文本框。每个文本框的 LostFocus-Event 都会启动后台程序将数据发送到连接的串行端口。
private readonly BackgroundWorker online_mode_send_worker = new BackgroundWorker();
online_mode_send_worker.DoWork += online_mode_send_worker_DoWork;
online_mode_send_worker.RunWorkerCompleted += online_mode_send_worker_RunWorkerCompleted;
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
online_mode_send_worker.RunWorkerAsync(data);
}
private void online_mode_send_worker_DoWork(object sender, DoWorkEventArgs e)
{
List<object> data = (List<object>)e.Argument;
Port.WriteLine(STARTCHARACTER + XMLSET + XML_TAG_START + data[0] + XML_TAG_STOP + data[1] + ENDCHARACTER);
string received = Port.ReadLine();
}
private void online_mode_send_worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//do some things after worker completed
}
至此,一切正常。
但有时我必须一个接一个地发送两个数据点,这时我遇到了问题。
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
online_mode_send_worker.RunWorkerAsync(data1);
//wait until backgroundworker has finished
online_mode_send_worker.RunWorkerAsync(data2);
}
Backgroundworker
仍然是 运行,我抛出了一个异常。
是否可以等到第一个 online_mode_send_worker.RunWorkerAsync(data)
完成后再开始第二个 online_mode_send_worker.RunWorkerAsync(data)
?
while(online_mode_send_worker.isBusy);
不工作,因为主线程正在阻塞并且 RunWorkerCompleted()
没有被抛出,所以 Backgroundwoker
总是很忙。
我找到了类似的东西,但是 Application.DoEvents()
在 wpf 中不可用。
while (online_mode_send_worker.IsBusy)
{
Application.DoEvents();
System.Threading.Thread.Sleep(100);
}
看来我错过了连载的东西。所以你要做的是同步你的异步调用:
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Run(() => mySerialDevice1.WriteData(data1));
Task.Run(() => mySerialDevice1.WriteData(data2));
}
public class SerialDevice
{
public Port Port { get; set; }
public object _LockWriteData = new object();
public void WriteData(string data)
{
lock(_LockWriteData)
{
Port.WriteLine(data);
}
}
}
另见:
- https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx
- https://msdn.microsoft.com/en-us/library/de0542zz(v=vs.110).aspx
原始答案
您可以使用 Task
而不是 Backgroundworker。
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Run(() => OnlineModeSendData(data1));
Task.Run(() => OnlineModeSendData(data2));
}
private void OnlineModeSendData(List<string> data)
{
Port.WriteLine(STARTCHARACTER + XMLSET + XML_TAG_START + data[0]+ XML_TAG_STOP + data[1] + ENDCHARACTER);
string received = Port.ReadLine();
}
我还想建议您制作真实的对象,而不是将字符串数组作为参数传递。
例如发送 BlinkLedRequest:
public class BlinkLedRequest
{
public int LedId{get;set;}
public int DurationInMilliseconds {get;set}
}
和相应的方法:
public void SendBlinkLed(BlickLedRequest request)
{
....
}
这是我在评论中提到的粗略想法。
public class Messenger {
private readonly BackgroundWorker online_mode_send_worker = new BackgroundWorker();
private readonly ConcurrentQueue<object> messages;
public Messenger() {
messages = new ConcurrentQueue<object>();
online_mode_send_worker.DoWork += online_mode_send_worker_DoWork;
online_mode_send_worker.RunWorkerCompleted += online_mode_send_worker_RunWorkerCompleted;
}
public void SendAsync(object message) {
if (online_mode_send_worker.IsBusy) {
messages.Enqueue(message);
} else {
online_mode_send_worker.RunWorkerAsync(message);
}
}
public Action<object> MessageHandler = delegate { };
private void online_mode_send_worker_DoWork(object sender, DoWorkEventArgs e) {
if (MessageHandler != null)
MessageHandler(e.Argument);
}
private void online_mode_send_worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
object nextMessage = null;
if (messages.Count > 0 && messages.TryDequeue(out nextMessage)) {
online_mode_send_worker.RunWorkerAsync(nextMessage);
}
}
}
您有一个队列来保留在后台工作人员忙碌时发送的消息,并让工作人员在完成工作后检查队列中是否有任何待处理的消息。
信使可以这样用
private Messenger messenger = new Messenger();
private void Initialize() { //I would expect this to be in the constructor
messenger.MessageHandler = MessageHandler;
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
messenger.SendAsync(data);
}
private void MessageHandler(object message)
{
List<object> data = (List<object>)message;
Port.WriteLine(STARTCHARACTER + XMLSET + XML_TAG_START + data[0] + XML_TAG_STOP + data[1] + ENDCHARACTER);
string received = Port.ReadLine();
}
我认为你应该使用 RunWorkerCompleted 事件并添加一个委托:
online_mode_send_worker.RunWorkerCompleted += (s, ev) =>
{
if (ev.Error != null)
{
//log Exception
}
//if(conditionToBrake)
// return;
online_mode_send_worker.RunWorkerAsync(data2);
};
online_mode_send_worker.RunWorkerCompleted(data1);
确保你设置了条件以避免无限循环。
更新:
bw.RunWorkerAsync(data1);
//wait here
bw.RunWorkerAsync(data2);
不是一个好的方法,因为UI会在等待的时候被阻塞。更好:
bw.RunWorkerAsync(new object[] { data1, data2 }); //or new object[] { data1 } if no data2
原回答:
我建议不要使用构造:while (bw.Busy) { ... }
(它会消耗cpu时间),使用同步对象,例如ManualResetEvent
BackgroundWorker 很棒class,但不支持等待。只需创建等待的添加对象:
var bw = new BackgroundWorker();
bw.DoWork += Bw_DoWork;
bw.RunWorkerCompleted += Bw_RunWorkerCompleted;
bool wasError;
ManualResetEvent e = null;
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
if (e != null)
return;
wasError = false;
e = new ManualResetEvent(false); //not signaled
bw.RunWorkerAsync(data1);
e.Wait(); //much better than while(bw.Busy())
if (!wasError)
bw.RunWorkerAsync(data2);
e = null;
}
private void Bw_DoWork(object sender, DoWorkEventArgs e)
{
//background work in another thread
}
private void Bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
//catch exception here
wasError = true;
}
e.Set(); //switch to signaled
}
我想说的是,如果您必须等到第一个 "job" 完成之后,您想要的是 Task.ContinueWith()
并相应地更改您的界面。 IMO 的 msdn page is good,但请注意您正在等待 "correct" 任务对象。提示:您应该在 Wait()
上调用 ContinueWith()
的 return 值。这是启动 Task
然后等待它的好模式,只要你可以保留 returned 的 Task
,这样你就可以等待它。
对于更通用的 "I only want one background thread doing things in the order they're added, and I want to wait until they're ALL done and I know when I'm done adding.",我建议使用只有一个线程使用它们的 BlockingCollection<Action>
。 in this other answer.
如果您只需要调用两次,您可以这样做:
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
online_mode_send_worker.RunWorkerAsync(data2);
}
但是,如果您需要对命令进行排队,则需要使用任务以另一种方式重写。 一个任务,在其中你将有一个 for 循环,你将在其中通过串行端口顺序发送数据。
https://msdn.microsoft.com/pt-br/library/system.threading.tasks.task(v=vs.110).aspx