wpf c#后台工作人员等待完成

wpf c#后台工作人员等待完成,c#,wpf,backgroundworker,C#,Wpf,Backgroundworker,我的wpf应用程序中有几个文本框。每个文本框的LostFocus事件启动backgroundworker将数据发送到连接的串行端口 private readonly BackgroundWorker online_mode_send_worker = new BackgroundWorker(); online_mode_send_worker.DoWork += online_mode_send_worker_DoWork; online_mode_send_worker.RunWorkerC

我的wpf应用程序中有几个文本框。每个文本框的LostFocus事件启动backgroundworker将数据发送到连接的串行端口

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
}
Backgroundworker
仍在运行,我收到一个异常抛出。 是否可以在第一个
联机\u模式\u发送\u worker.RunWorkerAsync(数据)
完成后等待,然后启动第二个
联机\u模式\u发送\u worker.RunWorkerAsync(数据)

while(在线模式下发送工作人员,isBusy)不工作,因为主线程正在阻塞,并且未抛出
RunWorkerCompleted()
,因此
backgroundroker
总是很忙

我发现了类似的内容,但是
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);
        }
    }
}
另见:

原始答案

您可以使用
任务
而不是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();
}
以及相应的方法:

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);
        }
    }

}
公共类信使{
私有只读BackgroundWorker online_mode_send_worker=新BackgroundWorker();
私有只读ConcurrentQueue消息;
公共信使(){
消息=新的ConcurrentQueue();
在线模式发送工作者工作+=在线模式发送工作者工作;
在线模式发送工作人员运行工作完成+=在线模式发送工作人员运行工作完成;
}
公共无效SendAsync(对象消息){
如果(联机模式发送工作人员。IsBusy){
消息。排队(消息);
}否则{
联机模式发送工作者运行工作同步(消息);
}
}
public Action MessageHandler=委托{};
私有无效联机\u模式\u发送\u工作者\u工作(对象发送者,工作目标){
if(MessageHandler!=null)
MessageHandler(例如参数);
}
private void online_mode_send_worker_runworker completed(对象发送方,runworker completedeventarge){
对象nextMessage=null;
if(messages.Count>0&&messages.TryDequeue(out nextMessage)){
联机模式发送工作程序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();
}
private Messenger=new Messenger();
private void Initialize(){//我希望它在构造函数中
messenger.MessageHandler=MessageHandler;
}
私有void文本框\u LostFocus(对象发送方,RoutedEventArgs e)
{
sendsync(数据);
}
私有void消息处理程序(对象消息)
{
列表数据=(列表)消息;
Port.WriteLine(STARTCHARACTER+XMLSET+XML\u TAG\u START+data[0]+XML\u TAG\u STOP+data[1]+ENDCHARACTER);
接收到的字符串=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
原始答复:

我建议不要使用构造:
而(bw.Busy){…}
(它会消耗cpu时间),请使用同步对象,例如ManualResetEvent

BackgroundWorker是一个很好的类,但不支持等待。只需为等待创建添加对象:

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
}

我想说的是,如果您必须等到第一个“作业”完成后,那么您需要的是
Task.ContinueWith()
,并相应地更改您的界面。在我看来,这是最好的选择,但请注意,您正在等待“正确”的任务对象。提示:应该调用
Wait()
ContinueWith()的返回值。这是一个很好的模式,可以启动
任务
,然后等待它,只要您可以保留返回的
任务
,以便您可以等待它


对于更通用的“我只希望一个后台线程按照添加顺序执行操作,我希望等到它们全部完成,并且我知道何时完成添加。”我建议使用
BlockingCollection。

如果您只需要调用两次,您可以这样做:

 bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);

    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        online_mode_send_worker.RunWorkerAsync(data2);
    }
但如果您需要将命令排队,则需要使用Task以另一种方式重写。 一个任务,其中您将有一个for循环,您将通过串行端口顺序发送数据


除非已经存在一些东西,否则我认为您可以创建一个封装后台工作程序的服务。此类还将有一个队列。保存任何数据。当工作开始时,工人处理数据。如果要发送更多数据,请将其添加到队列中。当工作人员完成时,它会检查队列以确定是否还有更多的工作要做,并退出下一个作业的队列,然后再次调用后台工作人员,否则将正常完成。请使用“任务”而不是Backgroundworker。使用后台工作人员似乎真的过时了。请参阅msdn:您似乎认为串行端口是线程安全的可重入端口。它也不是Task.Run()的目标?不要这样做。或者严格编辑它。闪烁的灯光可能有用,但与问题无关。我很难找到具体的问题,我相信它在这种情况下会起作用。我的主要抱怨仍然是,这是一个非标准的、稍微过于复杂的解决方案。还是a+1。
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
}
 bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);

    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        online_mode_send_worker.RunWorkerAsync(data2);
    }