Best way to report flow

I have a program that uses threads to execute time-consuming processes sequentially. I want to track the progress of each thread, similar to how the BackgroundWorker.ReportProgress / ProgressChanged model does. I cannot use ThreadPool or BackgroundWorker due to other restrictions that I was in. What is the best way to enable / disclose this functionality. Overload the Thread class and add a property / event? Another elegant solution?

+6
multithreading c # backgroundworker threadpool
source share
4 answers

Overload the Thread class and add a property / event?

If "overload" actually means inheritance, then no. Thread sealed, so it cannot be inherited, which means that you cannot add any properties or events to it.

Another elegant solution?

Create a class that encapsulates the logic that will be executed by the thread. Add a property or event (or both) that you can use to get progress information.

 public class Worker { private Thread m_Thread = new Thread(Run); public event EventHandler<ProgressEventArgs> Progress; public void Start() { m_Thread.Start(); } private void Run() { while (true) { // Do some work. OnProgress(new ProgressEventArgs(...)); // Do some work. } } private void OnProgress(ProgressEventArgs args) { // Get a copy of the multicast delegate so that we can do the // null check and invocation safely. This works because delegates are // immutable. Remember to create a memory barrier so that a fresh read // of the delegate occurs everytime. This is done via a simple lock below. EventHandler<ProgressEventArgs> local; lock (this) { var local = Progress; } if (local != null) { local(this, args); } } } 

Update:

Let me explain why a memory barrier is needed in this situation. An obstacle prevents reading from moving before other instructions. The most likely optimization does not come from the CPU, but from the JIT compiler, which β€œraises” the reading of Progress outside the while . This movement gives the impression of "obsolete" readings. Here is a semi-realistic demonstration of the problem.

 class Program { static event EventHandler Progress; static void Main(string[] args) { var thread = new Thread( () => { var local = GetEvent(); while (local == null) { local = GetEvent(); } }); thread.Start(); Thread.Sleep(1000); Progress += (s, a) => { Console.WriteLine("Progress"); }; thread.Join(); Console.WriteLine("Stopped"); Console.ReadLine(); } static EventHandler GetEvent() { //Thread.MemoryBarrier(); var local = Progress; return local; } } 

It is imperative that the Release build run without the vshost process. Any of them will disable the optimization, which will cause an error (I believe that this does not play in versions 1.0 and 1.1 due to their more primitive optimization). The error is that "Stopped" is never displayed, although it clearly should be. Now uncomment the Thread.MemoryBarrier call and notice the change in behavior. Also keep in mind that even the most subtle changes in the structure of this code currently impede the compiler's ability to do this optimization. One such change would actually be to call a delegate. In other words, you cannot currently reproduce the obsolete reading problem using a null check followed by a call pattern, but there is no CLI specification (which I know anyway), which prevents the future hypothetical JIT compiler from reusing this "lift ", optimization.

+11
source share

I tried it a while ago and it worked for me.

  • Create a List class with locks.
  • Add your threads to the instance of the class that you created.
  • Put a timer on your form or where you want to record a log / progress.
  • Enter the code in the Timer.Tick event to read the messages displayed by the streams.
+1
source share

You can also check the Asynchronous Event-Based Pattern .

+1
source share

Provide each thread with a callback that returns a state object. You can use the ManagedThreadId thread to track individual threads, for example, use it as a key to Dictionary<int, object> . You can call a callback from numerous places in the thread processing loop or call it from a timer that starts from a thread.

You can also use the return argument for a callback to signal the thread to pause or stop.

I have used callbacks with great success.

0
source share

All Articles