wpf c#backgroundworker等到完成

时间:2017-01-10 13:54:52

标签: c# wpf backgroundworker

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

6 个答案:

答案 0 :(得分:2)

以下是我在评论中提到的内容的粗略概念。

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

答案 1 :(得分:0)

似乎我错过了连续剧。所以你想要做的是同步你的异步调用:

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

另见:

原始回答

您可以使用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)
{
....
}

答案 2 :(得分:0)

我认为你应该使用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);

确保放置条件以避免无限循环。

答案 3 :(得分:0)

更新

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很棒,但不支持等待。只需创建等待的附加对象:

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
}

答案 4 :(得分:0)

我要说如果你必须等到第一个“工作”完成后,你想要的是Task.ContinueWith()并相应地改变你的界面。 IMO的msdn page is good,但请注意您正在等待“正确”的任务对象。提示:这是ContinueWith()的返回值,您应该调用Wait()。这是一个很好的模式,用于启动Task,然后等待它,只要您可以保留返回的Task,以便您可以等待它。

对于更通用的“我只希望一个后台线程按照它们添加的顺序执行操作,我想等到它们全部完成后我知道我什么时候完成添加。”我建议使用BlockingCollection<Action>,只有一个线程使用它们。如何执行该操作的示例是in this other answer

答案 5 :(得分:0)

如果您只需要拨打两次电话,则可以执行此操作:

 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