Shaitan00
Shaitan00

Reputation: 1341

How can a child thread notify a parent thread of its status/progress?

I have a service responsible for many tasks, one of which is to launch jobs (one at a time) on a separate thread (threadJob child), these jobs can take a fair amount of time and

have various phases to them which I need to report back.

Ever so often a calling application requests the status from the service (GetStatus), this means that somehow the service needs to know at what point the job (child thread) is

at, my hope was that at some milestones the child thread could somehow inform (SetStatus) the parent thread (service) of its status and the service could return that information

to the calling application.

For example - I was looking to do something like this:

class Service
{
private Thread threadJob;
private int JOB_STATUS;

public Service()
    {
    JOB_STATUS = "IDLE";
    }

public void RunTask()
    {
    threadJob = new Thread(new ThreadStart(PerformWork));
    threadJob.IsBackground = true;
    threadJob.Start();
    }

public void PerformWork()
    {
    SetStatus("STARTING");
    // do some work //
    SetStatus("PHASE I");
    // do some work //
    SetStatus("PHASE II");
    // do some work //
    SetStatus("PHASE III");
    // do some work //
    SetStatus("FINISHED");
    }

private void SetStatus(int status)
    {
    JOB_STATUS = status;
    }

public string GetStatus()
    {
    return JOB_STATUS;
    }

};

So, when a job needs to be performed RunTask() is called and this launches the thread (threadJob). This will run and perform some steps (using SetStatus to set the new status at

various points) and finally finish. Now, there is also function GetStatus() which should return the STATUS whenever requested (from a calling application using IPC) - this status

should reflect the current status of the job running by threadJob.

So, my problem is simple enough... How can threadJob (or more specifically PerformWork()) return to Service the change in status in a thread-safe manner (I assume my example above of SetStatus/GetStatus is

unsafe)? Do I need to use events? I assume I cannot simply change JOB_STATUS directly ... Should I use a LOCK (if so on what?)...

Upvotes: 6

Views: 9001

Answers (7)

Brian Gideon
Brian Gideon

Reputation: 48949

You could create an event in the Service class and then invoke it in a thread-safe manner. Pay very close attention to how I have implemented the SetStatus method.

class Service
{
  public delegate void JobStatusChangeHandler(string status);

  // Event add/remove auto implemented code is already thread-safe.
  public event JobStatusChangeHandler JobStatusChange;

  public void PerformWork()
  {
    SetStatus("STARTING");
    // stuff
    SetStatus("FINISHED");
  }

  private void SetStatus(string status)
  {
    JobStatusChangeHandler snapshot;
    lock (this)
    {
      // Get a snapshot of the invocation list for the event handler.
      snapshot = JobStatusChange;
    }

    // This is threadsafe because multicast delegates are immutable.
    // If you did not extract the invocation list into a local variable then
    // the event may have all delegates removed after the check for null which
    // which would result in a NullReferenceException when you attempt to invoke
    // it.
    if (snapshot != null)
    {
      snapshot(status);
    }
  }
}

Upvotes: 2

Ruben
Ruben

Reputation: 15505

Your current code mixes strings and ints for JOB_STATUS, which can't work. I'm assuming strings here, but it doesn't really matter, as I'll explain.


You current implementation is thread safe in the sense that no memory corruption will occur, since all assignments to reference type fields are guaranteed to be atomic. The CLR demands this, otherwise you could potentially access unmanaged memory if you could somehow access partially updated references. Your processor gives you that atomicity for free, however.

So as long as you're using reference types like strings, you won't get any memory corruption. The same is true for primitives like ints (and smaller) and enums based on them. (Just avoid longs and bigger, and non-primitive value types such as nullable integers.)

But, that is not the end of the story: this implementation is not guaranteed to always represent the current state. The reason for this is that the thread that calls GetStatus might be looking at a stale copy of the JOB_STATUS field, because the assignment in SetState contains no so-called memory barrier. That is: the new value for JOB_STATUS need not be sent to your main RAM right away. There are several reasons why this can be delayed:

  1. Writing to main RAM is inherently slow (relatively speaking), which is the reason your processor has all kinds of buffers and L-something caches in the first place, so the processor usually delays memory synchronization. Not for very long, but it will probably delay. This can be quite noticeable on multicore processors, as these usually have separate caches per core.
  2. The JIT might have stored the value of JOB_STATUS in a register earlier on, as part of some optimization strategy. Again, registers are far more efficient to use than your main RAM. However, this does mean that it might not see changes early enough, as it's still looking at the old copy in the register. (We're not talking minutes here, but still.)

So, if you want to be 100% certain that each thread & processor core is immediately aware of the changed status, declare your field as volatile:

private volatile int JOB_STATUS;

Now, GetStatus/SetStatus, without any locking constructs, is truly thread safe, as volatile demands that the value is read from and written to main RAM immediately (or something 100% equivalent, if the processor can do that more efficiently).

Note that if you don't declare your field as volatile you must use synchronization primitives, such as lock, but generally speaking you need to use the synchronization primitives both Get and Set, otherwise you won't solve the problem that volatile fixes.

Mind you, as you're doing IPC calls to get the status, I'd wager that you won't ever actually be able to observe any difference between non-volatile and volatile, given the overhead of the IPC calls and the thread synchronizations undoubtedly performed behind the scenes.

For more information on volatile, see volatile (C#) on MSDN.

Upvotes: 0

smcameron
smcameron

Reputation: 2557

I once wrote an app that needed a marker showing the progress a thread was making. I just used a shared global variable between them. The parent would just read the value, and the thread would just update it. No need to synchronize as only the parent read it, and only the child wrote it atomically. As it happened the parent was redrawing things frequently enough anyhow that it didn't even need to be poked by the child when the child updated the variable. Sometimes the simplest possible way works well.

Upvotes: 0

Daniel Mošmondor
Daniel Mošmondor

Reputation: 19956

I would go with delegate/event from the thread to the caller. If caller was UI or somewhere on that lines, I would be nice to the message pump and use appropriate Invoke()s to serialize notifications with the UIs thread when required.

Upvotes: 0

PVitt
PVitt

Reputation: 11740

You can use the Event-Based Async Pattern.

Upvotes: 1

Steven Evers
Steven Evers

Reputation: 17196

I'd have the child thread raise a 'statusupdate' event, passing a struct with the information necessary for the parent and have the parent subscribe to it when launching it.

Upvotes: 2

Guy Starbuck
Guy Starbuck

Reputation: 21873

You may have already looked into this, but the BackgroundWorker class gives you a nice interface for running tasks on background threads, and provides events to hook into for notifications that progress has changed.

Upvotes: 8

Related Questions