暂停新的BackGroundWorker直到之前的完成

4
我在处理线程问题方面遇到了困难。问题出现在我通过foreach循环迭代时。
当设置this.Document时,应用程序会执行登录操作,该操作由事件触发,并需要几秒钟才能完成。在worker_RunWorkerCompleted方法中,我需要执行一些依赖当前登录信息的操作。
问题是,在我为第一个文件执行此操作之前,this.Document已经更改,使应用程序执行另一个登录。这样,我实际上无法执行我的操作。
我的问题是:如何暂停下一个线程,直到上一个线程完成。还有其他解决办法吗?
我尝试使用AutoResetEvent,但没有成功。我在RunWorkerAsync调用后立即设置了waitOne()和在RunWorkerCompleted中设置了.Set()。代码从未进入RunWorkerCompleted......。
下面是代码:
    public void Start(object obj)
    {
       try
       {
          foreach (KeyValuePair<string, Stream> pair in this.CollectionOfFiles)
          {
              Worker = new BackgroundWorker();
              Worker.DoWork += new DoWorkEventHandler(worker_DoWork);
              Worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);

          using (Stream stream = pair.Value)
              {
                primaryDocument = new Document(stream);

                DataHolderClass dataHolder = new DataHolderClass();
                dataHolder.FileName = pair.Key;
                dataHolder.Doc = secondaryDocument;

               //background thread call
                Worker.RunWorkerAsync(dataHolder);
              }
            }
          }

       catch (Exception ex)
       {
          // exception logic
}
       finally
       {
          // complete logic
       }
    }


    private void worker_DoWork(object sender, DoWorkEventArgs e)
    {
       DataHolderClass dataHolder = ((DataHolderClass)e.Argument);
       // setting this attribute triggers execution of login event
       this.Document = dataHolder.Doc;
       e.Result = (dataHolder);
    }


    private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
       // here I need to perform some actions that are depending on the current login
       DataHolderClass dataHolder = ((DataHolderClass)e.Result);
       this.eventAggregator.GetEvent<ActionEvent>().Publish(new Message(EMessageType.Info) { Title = dataHolder.FileName });
    }

当再次调用Worker.RunWorkerAsync(dataHolder)时,情况变得繁忙了。 - no9
我理解得对吗?设置文档会以某种方式触发启动吗? - gwiazdorrr
1
哦,天哪,你那里有一个非常棘手的竞态条件。如果你打算同时记录多次(是吗?),为什么要使用单个(this.Document)属性?这个设计看起来很奇怪。 - gwiazdorrr
这是一个不幸的情况...但设计无法改变... - no9
1
如果RunWorkerCompleted需要当前登录信息,则在同一线程中执行。如果它们在不同的线程中,则一个可能会在另一个之前完成,这就是线程设计的目的... - Dour High Arch
显示剩余3条评论
3个回答

0

第九个,

请尝试以下操作:

System.Threading.ManualResetEvent _busy = new System.Threading.ManualResetEvent(false);

void ResumeWorker() 
{
     // Start the worker if it isn't running
     if (!backgroundWorker1.IsBusy) backgroundWorker1.RunWorkerAsync(dataHolder);  
     // Unblock the worker 
     _busy.Set();
}

void PauseWorker() 
{
    // Block the worker
    _busy.Reset();
}

void CancelWorker() 
{
    if (backgroundWorker1.IsBusy) {
        // Set CancellationPending property to true
        backgroundWorker1.CancelAsync();
        // Unblock worker so it can see that
        _busy.Set();
    }
}

然后在你的代码中运行该方法。

如果可以的话,请告诉我:)


0
class SimpleWaitPulse
{
  static readonly object _locker = new object();
  static bool _go;

  static void Main()
  {                                // The new thread will block
    new Thread (Work).Start();     // because _go==false.

    Console.ReadLine();            // Wait for user to hit Enter

    lock (_locker)                 // Let's now wake up the thread by
    {                              // setting _go=true and pulsing.
      _go = true;
      Monitor.Pulse (_locker);
    }
  }

  static void Work()
  {
    lock (_locker)
      while (!_go)
        Monitor.Wait (_locker);    // Lock is released while we’re waiting

    Console.WriteLine ("Woken!!!");
  }
}

你能使用脉冲吗?取自:albahari.com的C#多线程编程


0

嗯,这个设计太糟糕了...但是如果必须坚持使用它,你可以在前一个工作线程中设置等待句柄,并在下一个工作线程中等待它。这是最小的修复方法,仍然相当丑陋:

public void Start(object obj)
{
    try
    {
        BackgroundWorker previousWorker = null;
        DataHolderClass previousWorkerParams = null;

        foreach (KeyValuePair<string, Stream> pair in this.CollectionOfFiles)
        {
            // signal event on previous worker RunWorkerCompleted event
            AutoResetEvent waitUntilCompleted = null;
            if (previousWorker != null)
            {
                waitUntilCompleted = new AutoResetEvent(false);
                previousWorker.RunWorkerCompleted += (o, e) => waitUntilCompleted.Set();

                // start the previous worker
                previousWorker.RunWorkerAsync(previousWorkerParams);
            }

            Worker = new BackgroundWorker();

            Worker.DoWork += (o, e) =>
            {
                // wait for the handle, if there is anything to wait for
                if (waitUntilCompleted != null)
                {
                    waitUntilCompleted.WaitOne();
                    waitUntilCompleted.Dispose();
                }
                worker_DoWork(o, e);
            };

            using (Stream stream = pair.Value)
            {
                primaryDocument = new Document(stream);

                DataHolderClass dataHolder = new DataHolderClass();
                dataHolder.FileName = pair.Key;
                dataHolder.Doc = secondaryDocument;

                // defer running this worker; we don't want it to finish
                // before adding additional completed handler
                previousWorkerParams = dataHolder;
            }

            previousWorker = Worker;
        }

        if (previousWorker != null)
        {
            previousWorker.RunWorkerAsync(previousWorkerParams);
        }
    }

    catch (Exception ex)
    {
        // exception logic
    }
    finally
    {
        // complete logic
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接