Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/336.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 线程-如何通过UI交互终止工作/后台线程_C#_Multithreading_Compact Framework_Thread Safety - Fatal编程技术网

C# 线程-如何通过UI交互终止工作/后台线程

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

紧凑型框架,Windows Mobile 6,C#

我正在尝试compact框架上的一些后台线程,并且有一个关于:终止工作线程的问题

代码

我有以下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;
        }
    }
}
…我所期望的是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 }