BackgroundWorker.ReportProgress/ProgressChanged
模型的方式。由于其他限制,我不能使用ThreadPool
或BackgroundWorker
。如何最好地允许/公开此功能?重载Thread
类并添加属性/事件?还有更优雅的解决方案吗?BackgroundWorker.ReportProgress/ProgressChanged
模型的方式。由于其他限制,我不能使用ThreadPool
或BackgroundWorker
。如何最好地允许/公开此功能?重载Thread
类并添加属性/事件?还有更优雅的解决方案吗?如何重载Thread类并添加属性/事件?
如果你的意思是继承而不是重载,那么答案是否定的。Thread类是密封的,因此无法继承它,这意味着你将无法添加任何属性或事件。
有没有更优雅的解决方案?
创建一个类来封装应该在线程中执行的逻辑。添加一个属性或事件(或者两者兼备),以便可以从中获取进度信息。
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);
}
}
}
更新:
让我更清楚地解释为什么在这种情况下需要内存屏障。屏障可以防止读取在其他指令之前被移动。最有可能的优化不是来自CPU,而是来自JIT编译器“提升”Progress
的读取超出while
循环。这种移动会给人一种“陈旧”的读取印象。以下是问题的半真实演示。
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;
}
}
Release版本必须在没有vshost进程的情况下运行。任何一个进程都会禁用将引发错误的优化(我认为这在框架版本1.0和1.1中也不可重现,因为它们有更原始的优化)。问题是即使明显应该显示“Stopped”,但其不会被显示。现在,请取消对Thread.MemoryBarrier
的调用并注意行为上的变化。还要记住,即使对此代码结构进行最微小的更改,也可能当前阻止编译器进行所需的优化。这样的更改之一是实际调用委托。换句话说,您目前无法使用空值检查后跟调用模式来重现过时读取的问题,但我不知道CLI规范中是否有任何禁止未来假设的JIT编译器重新应用该“提升”优化的规定。
我之前试过这个方法,对我有效。
List
。 Timer.Tick
事件中编写代码来读取线程输出的消息。为每个线程提供一个返回状态对象的回调函数。您可以使用线程的ManagedThreadId
来跟踪不同的线程,例如将其用作Dictionary<int, object>
的键。您可以从线程的处理循环中的多个位置调用回调函数,或者从线程内部触发的定时器中调用它。
您还可以使用回调函数的返回参数来向线程发出暂停或停止的信号。
我已经成功地使用了回调函数。