C# 线程-如何通过UI交互终止工作/后台线程
紧凑型框架,Windows Mobile 6,C# 我正在尝试compact框架上的一些后台线程,并且有一个关于:终止工作线程的问题 代码 我有以下ThreadWorker类(源代码),当执行该类时,将在某些点执行检查,以确定它是否应该退出C# 线程-如何通过UI交互终止工作/后台线程,c#,multithreading,compact-framework,thread-safety,C#,Multithreading,Compact Framework,Thread Safety,紧凑型框架,Windows Mobile 6,C# 我正在尝试compact框架上的一些后台线程,并且有一个关于:终止工作线程的问题 代码 我有以下ThreadWorker类(源代码),当执行该类时,将在某些点执行检查,以确定它是否应该退出 public class ThreadWorker { public event EventHandler<ProgressEventArgs> OnProgress; protected virtual void Prog
public class ThreadWorker
{
public event EventHandler<ProgressEventArgs> OnProgress;
protected virtual void Progress(ProgressEventArgs args)
{
if (OnProgress != null)
OnProgress(this, args);
}
private void DoLongProcess()
{
// This will take a long time.
Thread.Sleep(15000);
Progress(new ProgressEventArgs("Some info for the UI to display."));
Thread.Sleep(15000);
}
public void DoSomeBackgroundWork()
{
try
{
while (!Stopping)
{
DoLongProcess();
if (Stopping) return;
DoLongProcess();
if (Stopping) return;
DoLongProcess();
if (Stopping) return;
DoLongProcess();
if (Stopping) return;
}
}
finally
{
SetStopped();
}
Console.WriteLine("DoSomeBackgroundWork: Terminating gracefully.");
}
/// <summary>
/// Lock covering stopping and stopped
/// </summary>
readonly object locker = new object();
/// <summary>
/// Whether or not the worker thread has been asked to stop
/// </summary>
bool stopping = false;
/// <summary>
/// Whether or not the worker thread has stopped
/// </summary>
bool stopped = false;
/// <summary>
/// Returns whether the worker thread has been asked to stop.
/// This continues to return true even after the thread has stopped.
/// </summary>
public bool Stopping
{
get
{
lock (locker)
{
return stopping;
}
}
}
/// <summary>
/// Returns whether the worker thread has stopped.
/// </summary>
public bool Stopped
{
get
{
lock (locker)
{
return stopped;
}
}
}
/// <summary>
/// Tells the worker thread to stop, typically after completing its
/// current work item. (The thread is *not* guaranteed to have stopped
/// by the time this method returns.)
/// </summary>
public void Stop()
{
lock (locker)
{
stopping = true;
}
}
/// <summary>
/// Called by the worker thread to indicate when it has stopped.
/// </summary>
void SetStopped()
{
lock (locker)
{
stopped = true;
}
}
}
…我所期望的是ThreadWorker继续执行其当前的DoLongProcess(),直到完成为止,并且仍然通过OnProgress事件处理程序和myThreadWorker_OnProgress将事件引发到UI
然而,实际发生的情况是,当OnProgress被提升时,应用程序会冻结在线读取
Invoke(new InsertToListBoxDelegate(InsertToListBox), new object[] { text });
问题
我怎么称呼
myThreadWorker.Stop();
t.Join();
…并且在后台线程终止之前仍然响应事件?只需将调用替换为BeginInvoke即可。如果这是不可能的,并且必须是同步的,请复制本文中的技巧: 将t.Join()替换为以下循环: for(;;) { if ( t.Join(100) ) // set appropriate timeout { break; } Application.DoEvents(); // resolve deadlock } 对于(;;) { if(t.Join(100))//设置适当的超时 { 打破 } Application.DoEvents();//解决死锁 }
通过调用
Thread.Join
您已经阻止了UI线程。通过调用Control.Invoke
您已经阻止了工作线程<代码>调用将消息发布到UI线程的消息队列并等待处理。但是,由于UI线程在等待工作线程完成时被阻塞,因此它无法开始执行委托,这将强制工作线程暂停。线程现在处于死锁状态
最大的问题是Join
调用。最好避免从UI线程调用Join
。相反,请在单击停止按钮后禁用该按钮,以便向用户提供停止请求已被接受且处于挂起状态的反馈。您甚至可能希望在状态栏上显示一条简单的消息,说明尽可能多的内容以使其清楚。然后,当引发最后一个OnProgress
事件时,这将是线程终止的信号,您可以重置表单上的所有内容
然而,你可能想考虑思想上的彻底转变。我认为Control.Invoke
方法被过度使用了。不要使用Control.Invoke
将事件处理程序的执行封送回UI线程,您可以使用计时器让UI线程轮询进度信息。当新的进度信息可用时,工作人员会将其发布到某个变量或数据结构中。这有几个优点
- 它打破了UI和
强加的工作线程之间的紧密耦合Control.Invoke
- 它将更新UI线程的责任放在它应该属于的UI线程上
- UI线程可以指定更新的时间和频率
- UI消息泵不会像工作线程启动的封送处理技术那样溢出
- 工作线程不必等待更新已执行的确认,然后再继续执行下一步(即,在UI和工作线程上都可以获得更高的吞吐量)
- 不要在UI线程上使用
Join
!这是一个完美的例子,人们可以打破任何编程模型。。。取消后台工作程序的干净方法是安装CancellationTokenSource
并向后台工作程序传递CancellationToken
的实例。您可以调用CancellationTokenSource。如果应该取消后台操作,请对已安装的实例取消。然后,您可以随意检查令牌的值,并定期离开当前的执行路径。我还建议使用更高级别的异步API(Tasks,APM),而不是手动生成线程。非常感谢Brian,第1段确实帮助澄清了一些困惑。还感谢您提出的其他解决方案的想法-我将尝试一下……我还不知道是什么/如何解决(我希望后面还有更多问题!!!!)再次感谢。Etfirfax你能详细解释一下为什么它会“破坏任何编程模型”吗?我看到的所有示例都使用join!!此外,我认为compact框架中没有CancellationToken。
myThreadWorker.Stop();
t.Join();
for(;;)
{
if ( t.Join(100) ) // set appropriate timeout
{
break;
}
Application.DoEvents(); // resolve deadlock
}