如何解决线程-如何通过UI交互终止工作/后台线程
|| Compact Framework,Windows Mobile 6,C#。 我正在使用紧凑型框架上的一些后台线程,并且有一个问题要问:终止工作线程。 代码 我有以下ThreadWorker类(此处的代码),该类在执行时将在某些点执行检查,以查看是否应该退出。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;
}
}
}
...以下形式启动线程...
public partial class Test : Form
{
public Test()
{
InitializeComponent();
}
private ThreadWorker myThreadWorker;
private Thread t = null;
private void Test_Load(object sender,EventArgs e)
{
myThreadWorker = new ThreadWorker();
myThreadWorker.OnProgress += new EventHandler<ProgressEventArgs>(myThreadWorker_OnProgress);
}
private void miStart_Click(object sender,EventArgs e)
{
try
{
listResults.Items.Insert(0,\"Set-up Thread.\");
t = new Thread(myThreadWorker.DoSomeBackgroundWork);
t.Name = \"My Thread\";
t.Priority = ThreadPriority.BelowNormal;
t.Start();
listResults.Items.Insert(0,\"Thread started.\");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void miStop_Click(object sender,\"Waiting for My Thread to terminate.\");
listResults.Refresh();
myThreadWorker.Stop();
t.Join();
listResults.Items.Insert(0,\"My Thread Finished.\");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private delegate void InsertToListBoxDelegate(String text);
private void InsertToListBox(String text)
{
if (InvokeRequired)
Invoke(new InsertToListBoxDelegate(InsertToListBox),new object[] { text });
else
{
listResults.Items.Insert(0,\"{0}\".With(text));
listResults.Refresh();
}
}
void myThreadWorker_OnProgress(object sender,ProgressEventArgs e)
{
InsertToListBox(e.Text);
}
}
问题
当我单击“停止”按钮时,它将调用...
myThreadWorker.Stop();
t.Join();
listResults.Items.Insert(0,\"My Thread Finished.\");
...我期望的是ThreadWorker继续其当前的DoLongProcess()直到完成,然后仍然通过OnProgress事件处理程序和myThreadWorker_OnProgress向UI引发事件。
但是,实际上发生的是,当引发OnProgress时,应用程序冻结在行读取中...
Invoke(new InsertToListBoxDelegate(InsertToListBox),new object[] { text });
问题
我怎么称呼...
myThreadWorker.Stop();
t.Join();
...并且仍然响应来自后台线程的事件,直到它终止?
解决方法
通过调用ѭ5,您已阻止UI线程。通过调用
Control.Invoke
,您已经阻止了工作线程。 Invoke
将消息发布到UI线程的消息队列中,并等待其处理。但是,由于UI线程被阻塞,等待工作线程完成,因此它无法开始执行委托,从而使工作线程停顿。现在线程已死锁。
最大的问题是Join
跟注。最好避免从UI线程调用Join
。相反,请在单击停止按钮后禁用该按钮,以向用户提供停止请求已接受且正在等待的反馈。您甚至可能希望在状态栏上显示一条简单的消息,说明要清楚得多。然后,当最后一个OnProgress
事件引发时,将表明线程已终止,您可以重置表单上的所有内容。
但是,您可能需要考虑思想上的根本转变。我认为“ 6”方法学被过度使用了。与其使用“ 6”将事件处理程序的执行编组回UI线程,还不如让UI线程使用计时器来轮询进度信息。当有新的进度信息可用时,工作人员会将其发布到某些变量或数据结构中。这有几个优点。
它打破了“ 6”强加的UI和工作线程之间的紧密耦合。
它将更新UI线程的责任放在了它应该属于的UI线程上。
UI线程可以指示更新的时间和频率。
UI消息泵不会像工作线程发起的封送处理技术那样被超限运行。
辅助线程在继续下一步之前不必等待确认已执行更新(即,UI和辅助线程上的吞吐量都更高)。
, 只需用BeginInvoke替换Invoke。如果这是不可能的并且必须是同步的,请从本文中复制DoEvents技巧:http://www.codeproject.com/KB/cs/workerthread.aspx。
用以下循环替换t.Join():
对于(;;)
{
if(t.Join(100))//设置合适的超时时间
{
打破;
}
Application.DoEvents(); //解决死锁
}
, 不要在UI线程上使用Join
!这是一个很好的示例,说明人们如何破坏任何编程模型...取消后台工作人员的干净方法是安装CancellationTokenSource
,然后向后台工作人员传递CancellationToken
实例。如果应取消后台操作,请在已安装的实例上调用“ 17”。然后,您可以随意检查令牌的值,然后定期离开当前执行路径。我还建议您使用更高级别的异步API(任务,APM),而不是手动生成线程。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。