线程-如何通过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),而不是手动生成线程。     

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...