开发者

synchronize threads - no UI

开发者 https://www.devze.com 2022-12-23 10:05 出处:网络
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'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:

  1. I don't have UI so I can't do beginInvoke (or use background thread) as I usually do in winforms.
  2. I thought to implement ISynchronizeInvoke on my main class but that looks a little bit overkill (maybe I'm wr开发者_C百科ong here).
  3. 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.
  4. Each job can take up to 20 minutes.
  5. 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.


You can also use lock to implement a thread-safe JobManager 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.

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();
    }
}

Example:

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);
        }
    }
}


You can notify the main thread of progress through a callback method. That is:

// in the main thread
public void ProgressCallback(int jobNumber, int status)
{
    // handle notification
}

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.


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 the ProgressChanged event are what you would use for progress updates.

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();
};


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消