我有一个多线程应用程序,用于从网站中提取数据。我希望能够从UI暂停和恢复多个线程。在网上搜索后,我开始了解可以用来控制(暂停/恢复)线程的两种方法。
使用Monitor类。
我有一个名为GetHtml
的函数,它只返回网站的html。为简洁起见,我只是展示了这个函数的小数部分。
public string GetHtml(string url, bool isProxy = false)
{
string result = "";
ExecutionGateway();
//->> EXTRA CODE FOR FETCHING HTML
return result;
}
我有一个函数ControlTasks
用于控制来自UI的线程,下面我已经使用ControlTasks
类以及{{1}使用两种线程控制方法解释了Monitor
函数我将简要解释函数EventWaitHandle
的工作原理。
1。使用ExecutionGateway
类
Monitor
从UI调用{p> private object taskStopper = new object();
public bool ControlTasks(bool isPause)
{
try
{
if (isPause)
{
Monitor.Enter(taskStopper);
}
else
{
Monitor.Exit(taskStopper);
}
return true;
}
catch (Exception ex)
{
Logger.Instance.WriteLog("ControlTasks:", ex, Logger.LogTypes.Error);
return false;
}
}
,如果ControlTasks
为真,则在对象isPause
上使用独占锁,否则释放锁,现在这里出现函数taskStopper
用于获取对象ExecutionGateway
的锁定,但它没有做任何事情,如下面的代码所示。
taskStopper
这样,当private void ExecutionGateway()
{
lock(taskStopper){ }
}
中isPause
为真时,所有正在运行的线程都进入等待状态,因为ControlTasks
被排他锁定,如果taskStopper
为假,则所有线程都会恢复其处理。
2。使用isPause
类
EventWaitHandle
此代码也从根本上执行相同的工作,其中根据private EventWaitHandle handle = new ManualResetEvent(true);
public bool ControlTasks(bool isPause)
{
try
{
if (isPause)
{
handle.Reset();
}
else
{
handle.Set();
}
return true;
}
catch (Exception ex)
{
Logger.Instance.WriteLog("ControlTasks:", ex, Logger.LogTypes.Error);
return false;
}
}
参数发信号/不发信号通知事件状态。现在,相应的isPause
方法。
ExecutionGateway
这两种方法有什么区别,一种比另一种更好?还有其他方法吗?
我多次面临的主要问题是,如果我使用上述方法中的任何一种,并且我有100个线程;当我暂停它们,然后在5分钟或更长时间后恢复它们,UI开始挂起。用户界面非常反应迟钝。它会得到更新但仍然保持挂起状态,并且每隔一段时间我就会收到“无响应”消息。有一件事我想提到每个线程提取数据并通知UI有关通过事件处理获取的数据。这种反应迟钝的原因是什么?这是我的方法的问题吗?
答案 0 :(得分:2)
我认为使用可明确传达您意图的构造总是可取的。你想要一个信号到他们应该<等待的其他线程(即停止做他们正在做的事情),直到你向他们发信号为止他们可以重新开始。您有一个控制线程(您的UI),并且可能有许多线程正在进行工作并将结果编组回UI。
方法1并不理想,因为锁(至少在我的经验中)最常用于保护不适合在多线程代码中使用的资源。例如,写入共享字段。
方法2更有意义,手动重置事件就像一个门:打开门,事物可以通过,关闭它,他们不能。这正是您正在寻找的行为,我认为大多数开发人员会很快理解这是您的意图。
至于你的第二个问题,听起来你正在收到堵塞用户界面的消息。如果你停止所有100个线程,然后同时启动它们,那么他们很有可能会非常接近地完成他们的工作并且所有人都试图将他们的工作结果发送到UI线。要解决此问题,您可以尝试在重新启动或使用较少线程时错开工作。另一种选择是聚合结果,并且每x秒仅调度一次UI - 但这样做的工作量更多。
答案 1 :(得分:1)
在选项1中,使用Monitor
类意味着一次只有一个线程拥有监视器对象的独占锁。这意味着在你的100个线程中,一次只处理1个,这种方法违背了使用线程的目的。这也意味着你的GUI线程必须等到当前工作线程完成才能获得锁定。
ManualResetEvent
是一个更好的选择,因为它用于线程之间的信号,而不是防止多线程访问。
我不知道为什么你的GUI使用第二个选项如此反应迟钝,但我不认为它与你的手动重置事件有关。更有可能你有一个不同的问题,GUI线程被淹没。你建议你有100个线程都向GUI发送通知事件,这似乎可能是罪魁祸首。
如果您调试应用程序会发生什么情况,并在GUI无响应时随机中断?多次这样做应该显示你的GUI线程是什么以及瓶颈在哪里。