views:

54

answers:

3

I'm new to multithreading in c# and am confused among all thread stuff. here is what I'm trying to have:

public class TheClass
{
    Thread _thread;
    bool _isQuitting;
    bool _isFinished;
    object jobData;
    public void Start()
    {
        jobData = new object();
        _thread = new Thread(new ThreadStart(Run));
        _thread.Start();
    }
    private void Run()
    {
        while (!_isQuitting)
        {
            lock (jobData) // or should i use mutex or monitor?
            {
                DoStuff();
            }
            // sleep till get triggered
        }
        DoCleanupStuff();
        _isFinished = true;
    }

    public void AddJob(object param)
    {
        jobData = param;
        //wake up the thread
    }
    public void Stop()
    {
        _isQuitting = true;
        //wake up & kill the thread
        //wait until _isFinished is true
        //dispose the _thread object
    }
}

in this class, what is the most efficient method to achieve commented out phrases and for overall performance?

A: 

I don't know about the performance, but it looks like you want AutoResetEvent.

This certainly gives you one of the simplest ways to do what you're describing. Test it out, and if you find performance is an issue, then worry about another way to do it.

JimG
+2  A: 

Consider using Monitor.Wait() and Monitor.Pulse().

For example:

object locker = new object();

private void Run()
{
    while (!_isQuitting)
    {
        lock (locker)
        {
            Monitor.Wait(locker);
            DoStuff(jobData);
        }
    }
    DoCleanupStuff();
}

public void AddJob(object param)
{
    // Obtain the lock first so that you don’t
    // change jobData while the thread is processing it
    lock (locker)
    {
        jobData = param;
        Monitor.Pulse(locker);
    }
}

public void Stop()
{
    lock (locker)
    {
        _isQuitting = true;
        Monitor.Pulse(locker);
    }

    // Wait for the thread to finish — no need for _isFinished
    _thread.Join();
}

However, notice that in my code you still have only one jobData object — the consequence being that AddJob() will wait for the current job to finish processing. This may not be what you want. Perhaps you should have a Queue<T> of job data objects; AddJob would Enqueue an item and Run would keep Dequeue’ing until the queue is empty. If you do this, the lock statement in Run should encompass only the access to the queue, not the entire processing stage.

Timwi
A: 

It looks to me like you want to use the producer/consumer pattern. This is most easily accomplished using a blocking queue. Once you have that then everything else is easy. You can find an implementation here or you can use the BlockingCollection class available in 4.0.

Note: Hardening code is omitted for brevity.

public class TheClass
{
    private Thread m_Thread;
    private BlockingCollection<object> m_Queue = new BlockingCollection<object>();
    private CancellationTokenSource m_Cancellation = new CancellationTokenSource();

    public void Start()
    {
        m_Thread = new Thread(new ThreadStart(Run));
        m_Thread.IsBackground = true;
        m_Thread.Start();
    }

    private void Run()
    {
        while (true)
        {
            try
            {
                object job = m_Queue.Take(m_Cancellation.Token);
            }
            catch (OperationCanceledException)
            {
                break;
            }
        }
    }

    public void AddJob(object param)
    {
        m_Queue.Add(param);
    }

    public void Stop()
    {
        m_Cancellation.Cancel();
        m_Thread.Join();
    }
}
Brian Gideon