我正在尝试编写多线程代码并面临一些同步问题。我知道这里有很多帖子,但我找不到合适的东西。
我有一个System.Timers.Timer
,它每隔30秒就会进入数据库并检查是否有新的工作。如果他找到了一个,他就会在当前线程上执行该作业(计时器为每个经过的时间打开新线程)。当作业正在运行时,我需要通知主线程(计时器所在的位置)有关进度的信息。
注意:
beginInvoke
(或使用后台线程)。ISynchronizeInvoke
,但这看起来有点矫枉过正(也许我在这里错了)。我的问题是:
通知我的主线程有关我的工作线程中的任何进展的正确方法是什么?
感谢您的帮助。
答案 0 :(得分:2)
您还可以使用lock
来实现一个线程安全的JobManager
类,该类跟踪有关不同工作线程的进度。在这个例子中,我只维护活动工作线程计数,但这可以扩展到您的进度报告需求。
class JobManager
{
private object synchObject = new object();
private int _ActiveJobCount;
public int ActiveJobsCount
{
get { lock (this.synchObject) { return _ActiveJobCount; } }
set { lock (this.synchObject) { _ActiveJobCount = value; } }
}
public void Start(Action job)
{
var timer = new System.Timers.Timer(1000);
timer.Elapsed += (sender, e) =>
{
this.ActiveJobsCount++;
job();
this.ActiveJobsCount--;
};
timer.Start();
}
}
示例:
class Program
{
public static void Main(string[] args)
{
var manager = new JobManager();
manager.Start(() => Thread.Sleep(3500));
while (true)
{
Console.WriteLine(manager.ActiveJobsCount);
Thread.Sleep(250);
}
}
}
答案 1 :(得分:1)
使用活动。例如,BackgroundWorker类专门针对您的想法而设计。
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
ReportProgress
函数以及ProgressChanged
事件将用于进度更新。
pullJobTimer.Elapsed += (sender,e) =>
{
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += (s,e) =>
{
// Whatever tasks you want to do
// worker.ReportProgress(percentComplete);
};
worker.ProgressChanged += mainThread.ProgressChangedEventHandler;
worker.RunWorkerAsync();
};
答案 2 :(得分:1)
您可以通过回调方法通知主线程的进度。那就是:
// in the main thread
public void ProgressCallback(int jobNumber, int status)
{
// handle notification
}
您可以在调用它时将该回调方法传递给工作线程(即作为委托),或者工作线程的代码可以隐式“知道”它。无论哪种方式都有效。
jobNumber和status参数只是示例。您可能希望使用其他方法来标识正在运行的作业,并且您可能希望使用枚举类型作为状态。无论如何,请注意ProgressCallback将由多个线程同时调用,因此如果您要更新任何共享数据结构或编写日志记录信息,则必须使用锁或其他同步技术保护这些资源。
您也可以使用事件,但保持主线程的事件订阅是最新的可能是一个潜在的问题。如果您忘记取消订阅特定工作线程事件中的主线程,您还可能会发生内存泄漏。虽然事件肯定会有用,但我会建议这个应用程序的回调。
答案 3 :(得分:1)
如果您不介意依赖.NET 3.0,可以使用Dispatcher来编组线程之间的请求。它的行为方式与Windows窗体中的Control.Invoke()类似,但没有Forms依赖项。您需要添加对WindowsBase程序集的引用(.NET 3.0及更新版本的一部分,并且是WPF的基础)
如果您不能依赖于.NET 3.0,那么我会说您从一开始就使用了正确的解决方案:在主类中实现ISynchronizeInvoke接口并传递到Timer的SynchronizingObject属性。然后将在主线程上调用您的计时器回调,然后可以生成BackgroundWorkers来检查数据库并运行任何排队的作业。作业将通过ProgressChanged事件报告进度,该事件将自动封送对主线程的调用。
快速谷歌搜索显示this example如何实际实现ISynchronizeInvoke界面。