最佳报告线程进度的方式 [英] Best way to report thread progress

查看:164
本文介绍了最佳报告线程进度的方式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用线程来顺序地执行耗时的过程的程序。我希望能够监控每个线程的类似的方式进步的 BackgroundWorker.ReportProgress / ProgressChanged 模型确实。我不能使用线程池的BackgroundWorker 由于其他方面的限制下我来的。是什么让最好的办法/公开此功能。超负荷类,并添加属性/事件?另一种更优雅的解决方案?


解决方案

重载Thread类,并添加一个
属性/事件?




如果通过超负荷你实际上意味着继承则没有。在是密封的,因此不能被继承,这意味着你将无法对任何属性或事件添加到它。




另外一个更优雅的解决方案?




创建一个封装了将要执行的逻辑类由该线程。添加可用于获取从中进度信息的属性或事件(或两者)。

 公共类工人
{
私人主题m_Thread =新主题(运行);

公共事件的EventHandler< ProgressEventArgs>进展;

公共无效启动()
{
m_Thread.Start();
}

私人无效的run()
{
,而(真)
{
//做一些工作。

OnProgress(新ProgressEventArgs(...));

//做一些工作。
}
}

私人无效OnProgress(ProgressEventArgs参数)
{

//获取多路广播委托的副本,让我们可以做
//空检查和调用安全。这工作,因为代表们
//不可改变的。请记住,这样委托的全新读
//每次发生创建一个内存屏障。这是通过下面简单的锁完成。
&事件处理LT; ProgressEventArgs>本地;
锁(本)
{
VAR本地=进展情况;
}
如果(地方!= NULL)
{
地方(这一点,参数);
}
}
}



更新:



让我为什么内存屏障是必要的,这种情况更加清楚一点。阻挡防止读出从之前的其它指令被移动。最有可能的优化是不是从CPU,但是JIT编译器揭进步读之外,而循环。这一运动给人陈旧的印象读取。这是问题的一个半现实的演示。

 类节目
{
静态事件事件处理进展;

静态无效的主要(字串[] args)
{
变种线程=新主题(
()=>
{
VAR本地= GetEvent();
,而(本地== NULL)
{
本地= GetEvent();
}
});
thread.Start();
Thread.sleep代码(1000);
+的进展=(S,A)=> {Console.WriteLine(进步); };
的Thread.join();
Console.WriteLine(停止);
到Console.ReadLine();
}

静态的事件处理程序GetEvent()
{
//Thread.MemoryBarrier();
VAR本地=进展情况;
返回当地;
}
}



当务之急是发布版本时没有跑vshost过程。每个人都会禁用体现的bug优化(我相信这并不是可再现的框架1.0和1.1版本,以及由于其更原始的优化)。该缺陷是停止从不显示,即使它显然应该的。现在,取消调用 Thread.MemoryBarrier 并注意行为的变化。也请记住,即使这个代码的结构中最细微的变化的目前的抑制编译器的使有问题的优化能力。一个这样的变化将是实际调用委托。换句话说,你不能的目前的使用空检查后跟一个调用模式重现读取过时的问题,但是的没有的CLI中的规范(即我所知道的呢),从重新应用解除禁止优化未来的假设JIT编译器。


I have a program that uses threads to perform time-consuming processes sequentially. I want to be able to monitor the progress of each thread similar to the way that the BackgroundWorker.ReportProgress/ProgressChanged model does. I can't use ThreadPool or BackgroundWorker due to other constraints I'm under. What is the best way to allow/expose this functionality. Overload the Thread class and add a property/event? Another more-elegant solution?

解决方案

Overload the Thread class and add a property/event?

If by "overload" you actually mean inherit then no. The Thread is sealed so it cannot be inherited which means you will not be able to add any properties or events to it.

Another more-elegant solution?

Create a class that encapsulates the logic that will be executed by the thread. Add a property or event (or both) which can be used to obtain progress information from it.

public class Worker
{
  private Thread m_Thread = new Thread(Run);

  public event EventHandler<ProgressEventArgs> Progress;

  public void Start()
  {
    m_Thread.Start();
  }

  private void Run()
  {
    while (true)
    {
      // Do some work.

      OnProgress(new ProgressEventArgs(...));

      // Do some work.
    }
  }

  private void OnProgress(ProgressEventArgs args)
  {

    // Get a copy of the multicast delegate so that we can do the
    // null check and invocation safely. This works because delegates are
    // immutable. Remember to create a memory barrier so that a fresh read
    // of the delegate occurs everytime. This is done via a simple lock below.
    EventHandler<ProgressEventArgs> local;
    lock (this)
    {
      var local = Progress;
    }
    if (local != null)
    {
      local(this, args);
    }
  }
}

Update:

Let me be a little more clear on why a memory barrier is necessary in this situation. The barrier prevents the read from being moved before other instructions. The most likely optimization is not from the CPU, but from the JIT compiler "lifting" the read of Progress outside of the while loop. This movement gives the impression of "stale" reads. Here is a semi-realistic demonstration of the problem.

class Program
{
    static event EventHandler Progress;

    static void Main(string[] args)
    {
        var thread = new Thread(
            () =>
            {
                var local = GetEvent();
                while (local == null)
                {
                    local = GetEvent();
                }
            });
        thread.Start();
        Thread.Sleep(1000);
        Progress += (s, a) => { Console.WriteLine("Progress"); };
        thread.Join();
        Console.WriteLine("Stopped");
        Console.ReadLine();
    }

    static EventHandler GetEvent()
    {
        //Thread.MemoryBarrier();
        var local = Progress;
        return local;
    }
}

It is imperative that a Release build is ran without the vshost process. Either one will disable the optimization that manifest the bug (I believe this is not reproducable in framework version 1.0 and 1.1 as well due to their more primitive optimizations). The bug is that "Stopped" is never displayed even though it clearly should be. Now, uncomment the call to Thread.MemoryBarrier and notice the change in behavior. Also keep in mind that even the most subtle changes to the structure of this code currently inhibit the compiler's ability to make the optimization in question. One such change would be to actually invoke the delegate. In other words you cannot currently reproduce the stale read problem using the null check followed by an invocation pattern, but there is nothing in the CLI specification (that I am aware of anyway) that prohibits a future hypothetical JIT compiler from reapplying that "lifting" optimization.

这篇关于最佳报告线程进度的方式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆