[英]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.