繁体   English   中英

C#BackgroundWorker

[英]C# BackgroundWorker

我有一个按钮,点击事件我从网络获得一些信息。

当我获取信息时,我解析它并向ListBox添加项目。 一切都很好,但是当我快速双击按钮时,似乎两个后台工作人员正在运行,并且在完成所有工作后,列表中的项目被公开。

我想这样做,如果你单击按钮并获取信息的过程正在工作,这个线程正在停止,只有在第一次工作完成后,第二个工作才开始。
是的,我知道AutoResetEvent,但是当我使用它时,它只帮助了我一次,而且从来没有更多。 我无法实现这种情况,希望你能帮助我!

现在我甚至尝试使更容易但没有成功:(:我添加了一个标志字段(RefreshDialogs)(默认为false),当用户点击按钮时,如果标志为真(这意味着工作正在进行),一切都没有,但是当flag字段设置为false时,一切都很好,我们开始一个新进程。
当Backgroundwork完成时,我将字段标志更改为false(这意味着用户可以运行新的过程)。

    private void Message_Refresh_Click(object sender, EventArgs e)
    {
        if (!RefreshDialogs)
        {
            RefreshDialogs = true;
            if (threadBackgroundDialogs.WorkerSupportsCancellation)
            {
                threadBackgroundDialogs.CancelAsync();
            }
            if (!threadBackgroundDialogs.IsBusy)
            {
                downloadedDialogs = 0;
                threadBackgroundDialogs = new BackgroundWorker();
                threadBackgroundDialogs.WorkerSupportsCancellation = true;
                threadBackgroundDialogs.DoWork += LoadDialogs;
                threadBackgroundDialogs.RunWorkerCompleted += ProcessCompleted;
                threadBackgroundDialogs.RunWorkerAsync();
            }
        }
    }
    void ProcessCompleted(object sender,  RunWorkerCompletedEventArgs e)
    {
        RefreshDialogs = false;
    }

所以你想在第一个进程运行时保持第二个进程运行,但它们不应该互相干扰? 在第一个完成后,第二个继续?

粗制方式:循环:

        if (!RefreshDialogs)
    {
        RefreshDialogs = true;

这成了:

    while(RefreshDialogs)
    {
    }
        RefreshDialogs = true;

设置为false后,第二个进程将跳出一段时间。 (注意这是极其无效的,因为两个进程都会一直运行,我很确定第二个进程会阻塞第一个进程,但现在多任务处理它不应该,如果阻塞使用Dispatcher.Thread)

优雅的方式:使用信号量

http://msdn.microsoft.com/de-de/library/system.threading.semaphore%28v=vs.80%29.aspx

如果你发现不可能让两个进程同时运行,或者想要另一种方式:添加一个Array / List / int,当第二个进程注意到第一个进程正在运行时,就像你的bool一样,增加你的Added变量,并在该过程结束时,重新启动新进程并减少变量:

    int number;

    if (!RefreshDialogs)
    {
        RefreshDialogs = true;
        your code;
        if(number > 0)
        {
          number--;
          restart process
        }
    }
    else
    {
    number++;
    }

我不得不承认,我最喜欢我的最后一个提案,因为它非常高效。

让你的线程阻塞。 这很容易;

lock(someSharedGlobalObject)
{
  Do Work, Exit early if cancelled
}

这样,其他线程将等到第一个线程释放锁定。 它们将永远不会同时执行并静默等待,直到它们能够继续。

至于其他选择; 为什么不在单击时禁用该按钮,并在后台工作完成时重新启用它。 唯一的问题是这不允许取消当前线程。 用户必须等待它完成。 它确实使任何并发很容易消失。

这种方法怎么样?

创建一个请求队列或计数器,每按一次按钮就会递增一次。 每次计数> 0时,启动后台工作程序。 当信息到来时,减少计数并检查0.如果它仍然> 0则重新启动工作人员。 因为您的请求处理程序变为顺序。

在这种方法中,您可能会遇到两个线程连续引用计数的问题,因为您可以使用锁定解锁条件。

我已经为我的应用程序采用了这种方法并且运行良好,希望它对您也一样。

使用可以使用信号量

  class TheClass
 {
    static SemaphoreSlim _sem = new SemaphoreSlim (3);

    static void Main()   
    {     
       for (int i = 1; i <= 5; i++) 
       new Thread (Enter).Start (i);   
    } 

    static void Enter (object name)   
    {     
      Console.WriteLine (name + " wants to enter");    
      _sem.Wait();     
      Console.WriteLine (name + " has entered!");               
      Thread.Sleep (1000 * (int) name );              
      Console.WriteLine (name + " is leaving");       
      _sem.Release();   } 
     }
 }

我不是Windows Phone专家,但我认为它支持TPL,所以下面的代码可以很好地阅读:

private object syncRoot =new object();
private Task latestTask;

public void EnqueueAction(System.Action action)
{
    lock (syncRoot)
    {
        if (latestTask == null)
            latestTask = Task.Factory.StartNew(action);
        else
            latestTask = latestTask.ContinueWith(tsk => action());
    }
}

我找到了解决方案并感谢@Giedrius。 仅当proccess在最后,当我向Listbox添加项目时,Flag RefreshingDialogs才设置为true。 我使用此标志的原因是,当从网络获取内容的异步操作(HttpWebRequest,方法BeginGetRequestStream)开始时,进程状态发生变化,但是在网络操作完成后我需要进行UI操作而不仅仅是它们(解析内容并将其添加到Listbox)我的解决方案是:

    private object syncRoot = new object();
    private Task latestTask;

    public void EnqueueAction(System.Action action)
    {
        lock (syncRoot)
        {
            if (latestTask == null)
            {
                downloadedDialogs = 0;
                latestTask = Task.Factory.StartNew(action);
            }
            else if(latestTask.IsCompleted && !RefreshingDialogs)
            {
                RefreshingDialogs = true;
                downloadedDialogs = 0;
                latestTask = Task.Factory.StartNew(action);
            }
        }
    }

    private void Message_Refresh_Click(object sender, EventArgs e)
    {
        Action ac = new Action(LoadDialogs2);
        EnqueueAction(ac);
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM