C# 同步线程-无用户界面
我正在尝试编写多线程代码,并面临一些同步问题。我知道这里有很多帖子,但我找不到合适的 我有一个C# 同步线程-无用户界面,c#,multithreading,synchronization,C#,Multithreading,Synchronization,我正在尝试编写多线程代码,并面临一些同步问题。我知道这里有很多帖子,但我找不到合适的 我有一个System.Timers.Timer,每隔30秒它就会进入数据库并检查是否有新的作业。如果他找到了一个,他将在当前线程上执行作业(计时器为每个经过的线程打开新线程)。当作业运行时,我需要将进度通知主线程(计时器所在的位置) 注: 我没有UI,所以我不能像通常在winforms中那样执行beginInvoke(或使用后台线程) 我想在我的主类上实现ISynchronizeInvoke,但这看起来有点过头
System.Timers.Timer
,每隔30秒它就会进入数据库并检查是否有新的作业。如果他找到了一个,他将在当前线程上执行作业(计时器为每个经过的线程打开新线程)。当作业运行时,我需要将进度通知主线程(计时器所在的位置)
注:
beginInvoke
(或使用后台线程)ISynchronizeInvoke
,但这看起来有点过头了(也许我在这里错了)感谢您的帮助。使用事件。例如,BackgroundWorker类是专门为您的想法而设计的
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();
};
您可以通过回调方法通知主线程进度。即:
// in the main thread
public void ProgressCallback(int jobNumber, int status)
{
// handle notification
}
您可以在调用该回调方法(即作为委托)时将其传递给工作线程,或者工作线程的代码可以隐式地“知道”该回调方法。两种方法都有效
jobNumber和status参数只是示例。您可能希望使用其他方法来标识正在运行的作业,并且可能希望使用枚举类型作为状态。无论如何,请注意ProgressCallback将由多个线程并发调用,因此,如果要更新任何共享数据结构或写入日志信息,则必须使用锁或其他同步技术来保护这些资源
您也可以为此使用事件,但保持主线程的事件订阅最新可能是一个潜在的问题。如果忘记从特定工作线程的事件中取消订阅主线程,则还可能发生内存泄漏。虽然事件肯定会起作用,但我建议此应用程序使用回调。如果您不介意依赖.NET 3.0,可以使用来封送线程之间的请求。它的行为方式与Windows窗体中的Control.Invoke()类似,但不具有窗体依赖关系。不过,您需要添加对WindowsBase程序集的引用(是.NET 3.0及更新版本的一部分,是WPF的基础) 如果您不能依赖.NET3.0,那么我认为您从一开始就找到了正确的解决方案:在主类中实现接口,并将其传递给计时器的属性。然后将在主线程上调用计时器回调,然后可以生成检查数据库并运行任何排队作业的线程。作业将通过事件报告进度,该事件将自动封送对主线程的调用
google快速搜索揭示了如何实际实现ISynchronizeInvoke接口。您还可以使用
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);
}
}
}
我觉得使用BackgroundWorker是错误的,我可以解释原因。System.Timers.Timer pullJobTimer=新的System.Timers.Timer(间隔);pullJobTimer.Appead+=新系统.Timers.ElapsedEventHandler(PullQJobs);我可以在pulljobs中创建一个新的后台工作人员,这是真的,然后我就会有所改变。但是计时器打开一个新线程,然后我将打开一个新的backgroundworker线程???感觉不对不?也许我不明白你的意思。你甚至不需要打开一个新的线程。当您通过
RunWorkerAsync()
异步运行BackgroundWorker时,BackgroundWorker会为您执行此操作。正如Jim Mischel所建议的,这也不会出现内存泄漏,因为事件订阅在被垃圾收集时会被BackgroundWorker实例破坏。我知道BackgroundWorker会打开一个线程,但也会打开计时器。所以,主类usetimer->Timer为每个经过的线程打开一个新线程->然后我运行backgroundworker打开另一个线程。这就是让我感到不舒服的地方。还要注意,ProgressChanged
事件处理程序将在线程池工作线程中运行,而不是在应用程序主线程上运行。@Jake,“System.Timers.Timer
类默认情况下将在从公共语言运行库(CLR)获取的工作线程上调用计时器事件处理程序。”线程池。“您不能通知线程。您可以通知一个对象。Jim,我认为这是同步调用,而我需要异步调用进程回调是在工作线程上调用的,而不是在主线程上调用的。因此,从主线程的角度来看,这是一个异步调用。