Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<blockquote> <p>Overload the Thread class and add a property/event?</p> </blockquote> <p>If by "overload" you actually mean inherit then no. The <code>Thread</code> is sealed so it cannot be inherited which means you will not be able to add any properties or events to it.</p> <blockquote> <p>Another more-elegant solution?</p> </blockquote> <p>Create a class that encapsulates the logic that will be executed by the thread. Add a property or event (or both) which can be used to obtain progress information from it.</p> <pre><code>public class Worker { private Thread m_Thread = new Thread(Run); public event EventHandler&lt;ProgressEventArgs&gt; 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&lt;ProgressEventArgs&gt; local; lock (this) { var local = Progress; } if (local != null) { local(this, args); } } } </code></pre> <p><strong>Update:</strong></p> <p>Let me be a little more clear on why a memory barrier is necessary in this situation. The barrier prevents the read from being moved before other instructions. The most likely optimization is not from the CPU, but from the JIT compiler "lifting" the read of <code>Progress</code> outside of the <code>while</code> loop. This movement gives the impression of "stale" reads. Here is a semi-realistic demonstration of the problem.</p> <pre><code>class Program { static event EventHandler Progress; static void Main(string[] args) { var thread = new Thread( () =&gt; { var local = GetEvent(); while (local == null) { local = GetEvent(); } }); thread.Start(); Thread.Sleep(1000); Progress += (s, a) =&gt; { Console.WriteLine("Progress"); }; thread.Join(); Console.WriteLine("Stopped"); Console.ReadLine(); } static EventHandler GetEvent() { //Thread.MemoryBarrier(); var local = Progress; return local; } } </code></pre> <p>It is imperative that a Release build is ran without the vshost process. Either one will disable the optimization that manifest the bug (I believe this is not reproducable in framework version 1.0 and 1.1 as well due to their more primitive optimizations). The bug is that "Stopped" is never displayed even though it clearly should be. Now, uncomment the call to <code>Thread.MemoryBarrier</code> and notice the change in behavior. Also keep in mind that even the most subtle changes to the structure of this code <em>currently</em> inhibit the compiler's ability to make the optimization in question. One such change would be to actually invoke the delegate. In other words you cannot <em>currently</em> reproduce the stale read problem using the null check followed by an invocation pattern, but there is <em>nothing</em> in the CLI specification (that I am aware of anyway) that prohibits a future hypothetical JIT compiler from reapplying that "lifting" optimization.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload