同步线程 - 无 UI
我正在尝试编写多线程代码并面临一些同步问题。我知道这里有很多帖子,但我找不到合适的内容。
我有一个 System.Timers.Timer,每 30 秒就会进入数据库并检查是否有任何新作业。如果他找到一个,他就会在当前线程上执行该作业(计时器在每次过去后打开新线程)。当作业运行时,我需要通知主线程(计时器所在的位置)有关进度。
注意:
- 我没有 UI,所以我无法像通常在 winforms 中那样执行
beginInvoke
(或使用后台线程)。 - 我想在我的主类上实现 ISynchronizeInvoke ,但这看起来有点矫枉过正(也许我错了)。
- 我的作业类中有一个事件,主类向它注册,并且我在需要时调用该事件,但我担心它可能会导致阻塞。
- 每个作业最多可能需要 20 分钟。
- 我最多可以同时运行 20 个作业。
我的问题是:
通知我的主线程有关工作线程中的任何进度的正确方法是什么?
感谢您的任何帮助。
I'm trying to write multithreading code and facing some synchronization questions. I know there are lots of posts here but I couldn't find anything that fits.
I have a System.Timers.Timer
that elapsed every 30 seconds it goes to the db and checks if there are any new jobs. If he finds one, he executes the job on the current thread (timer open new thread for every elapsed). While the job is running I need to notify the main thread (where the timer is) about the progress.
Notes:
- I don't have UI so I can't do
beginInvoke
(or use background thread) as I usually do in winforms. - I thought to implement
ISynchronizeInvoke
on my main class but that looks a little bit overkill (maybe I'm wrong here). - I have an event in my job class and the main class register to it and I invoke the event whenever I need but I'm worrying it might cause blocking.
- Each job can take up to 20 minutes.
- I can have up to 20 jobs running concurrently.
My question is:
What is the right way to notify my main thread about any progress in my job thread?
Thanks for any help.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您还可以使用
lock
实现线程安全的JobManager
类,用于跟踪不同工作线程的进度。在此示例中,我仅维护活动工作线程计数,但这可以扩展到您的进度报告需求。例子:
You can also use
lock
to implement a thread-safeJobManager
class that tracks progress about the different worker threads. In this example I just maintain the active worker threads count, but this can be extended to your progress reports needs.Example:
您可以通过回调方法通知主线程进度。也就是说:
当您调用它时(即作为委托),您可以将该回调方法传递给工作线程,或者工作线程的代码可以隐式“知道”它。无论哪种方式都有效。
jobNumber 和 status 参数只是示例。您可能希望使用其他方式来标识正在运行的作业,并且可能希望使用枚举类型来表示状态。无论您如何执行,请注意 ProgressCallback 将由多个线程同时调用,因此如果您要更新任何共享数据结构或写入日志记录信息,则必须使用锁或其他同步技术来保护这些资源。
您也可以使用事件来实现此目的,但保持主线程的事件订阅最新可能是一个潜在的问题。如果您忘记取消主线程对特定工作线程事件的订阅,也可能会出现内存泄漏。尽管事件肯定会起作用,但我建议此应用程序使用回调。
You can notify the main thread of progress through a callback method. That is:
You can pass that callback method to the worker thread when you invoke it (i.e. as a delegate), or the worker thread's code can "know" about it implicitly. Either way works.
The jobNumber and status parameters are just examples. You might want you use some other way to identify the jobs that are running, and you may want to use an enumerated type for the status. However you do it, be aware that the ProgressCallback will be called by multiple threads concurrently, so if you're updating any shared data structures or writing logging information, you'll have to protect those resources with locks or other synchronization techniques.
You can also use events for this, but keeping the main thread's event subscriptions up to date can be a potential problem. You also have the potential of a memory leak if you forget to unsubscribe the main thread from a particular worker thread's events. Although events would certainly work, I would recommend the callback for this application.
使用事件。例如,BackgroundWorker 类是专门为您的想法而设计的。
http://msdn.microsoft.com/en-us/ library/system.componentmodel.backgroundworker.aspx
ReportProgress
函数以及ProgressChanged
事件是您用于进度更新的函数。Use events. The BackgroundWorker class, for example, is designed specifically for what you have in mind.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
The
ReportProgress
function along with theProgressChanged
event are what you would use for progress updates.如果您不介意依赖.NET 3.0,您可以使用 调度程序来编组线程之间的请求。它的行为方式与 Windows 窗体中的 Control.Invoke() 类似,但不具有窗体依赖性。不过,您需要添加对 WindowsBase 程序集的引用(.NET 3.0 和更新版本的一部分,是 WPF 的基础)
如果您不能依赖 .NET 3.0 那么我会说您我们从一开始就采取了正确的解决方案:实施 ISynchronizeInvoke 主类中的接口并将其传递给 计时器的 SynchronizingObject 属性。然后,您的计时器回调将在主线程上调用,然后可以生成 BackgroundWorkers 检查数据库并运行任何排队的作业。这些作业将通过 ProgressChanged 报告进度事件将自动封送对主线程的调用。
通过 Google 快速搜索,发现了此示例,了解如何实际实现 ISynchronizeInvoke 接口。
If you don't mind depending on .NET 3.0 you can use the Dispatcher to marshal requests between threads. It behaves in a similar way to Control.Invoke() in Windows Forms but doesn't have the Forms dependency. You'll need to add a reference to the WindowsBase assembly though (part of .NET 3.0 and newer and is basis for WPF)
If you can't depend on .NET 3.0 then I'd say you were onto the correct solution from the beginning: Implement the ISynchronizeInvoke interface in your main class and pass that to the SynchronizingObject property of the Timer. Then your timer callback will be called on the main thread, which can then spawn BackgroundWorkers that checks the DB and runs any queued jobs. The jobs would report progress through the ProgressChanged event which will marshal the call to the main thread automatically.
A quick google search revealed this example on how to actually implement the ISynchronizeInvoke interface.