C# 同步线程-无用户界面

C# 同步线程-无用户界面,c#,multithreading,synchronization,C#,Multithreading,Synchronization,我正在尝试编写多线程代码,并面临一些同步问题。我知道这里有很多帖子,但我找不到合适的 我有一个System.Timers.Timer,每隔30秒它就会进入数据库并检查是否有新的作业。如果他找到了一个,他将在当前线程上执行作业(计时器为每个经过的线程打开新线程)。当作业运行时,我需要将进度通知主线程(计时器所在的位置) 注: 我没有UI,所以我不能像通常在winforms中那样执行beginInvoke(或使用后台线程) 我想在我的主类上实现ISynchronizeInvoke,但这看起来有点过头

我正在尝试编写多线程代码,并面临一些同步问题。我知道这里有很多帖子,但我找不到合适的

我有一个
System.Timers.Timer
,每隔30秒它就会进入数据库并检查是否有新的作业。如果他找到了一个,他将在当前线程上执行作业(计时器为每个经过的线程打开新线程)。当作业运行时,我需要将进度通知主线程(计时器所在的位置)

注:

  • 我没有UI,所以我不能像通常在winforms中那样执行
    beginInvoke
    (或使用后台线程)
  • 我想在我的主类上实现
    ISynchronizeInvoke
    ,但这看起来有点过头了(也许我在这里错了)
  • 我的job类中有一个事件,主类向它注册,我在需要时调用该事件,但我担心它可能会导致阻塞
  • 每项作业最多需要20分钟
  • 我最多可以同时运行20个作业
  • 我的问题是:

    什么是通知我的主线程我的工作线程的任何进展的正确方式


    感谢您的帮助。

    使用事件。例如,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,我认为这是同步调用,而我需要异步调用进程回调是在工作线程上调用的,而不是在主线程上调用的。因此,从主线程的角度来看,这是一个异步调用。