Search code examples
c#multithreadingautoresetevent

Two threads one core


I'm playing around with a simple console app that creates one thread and I do some inter thread communication between the main and the worker thread.

I'm posting objects from the main thread to a concurrent queue and the worker thread is dequeueing that and does some processing.

What strikes me as odd, is that when I profile this app, even despite I have two cores. One core is 100% free and the other core have done all the work, and I see that both threads have been running in that core. enter image description here

Why is this?

Is it because I use a wait handle that sets when I post a message and releases when the processing is done?

This is my sample code, now using 2 worker threads. It still behaves the same, main, worker1 and worker2 is running in the same core. Ideas?

[EDIT] It sort of works now, atleast, I get twice the performance compared to yesterday. the trick was to slow down the consumer just enough to avoid signaling using the AutoResetEvent.

public class SingleThreadDispatcher
{
    public long Count;
    private readonly ConcurrentQueue<Action> _queue = new ConcurrentQueue<Action>();
    private volatile bool _hasMoreTasks;
    private volatile bool _running = true;
    private int _status;
    private readonly AutoResetEvent _signal = new AutoResetEvent(false);
    public SingleThreadDispatcher()
    {
        var thread = new Thread(Run)
        {
            IsBackground = true,
            Name = "worker" + Guid.NewGuid(),           
        };

        thread.Start();
    }

    private void Run()
    {
        while (_running)
        {

            _signal.WaitOne();
            do
            {
                _hasMoreTasks = false;

                Action task;
                while (_queue.TryDequeue(out task) && _running)
                {
                    Count ++;
                    task();
                }
                //wait a short while to let _hasMoreTasks to maybe be set to true
                //this avoids the roundtrip to the AutoResetEvent
                //that is, if there is intense pressure on the pool, we let some new
                //tasks have the chance to arrive and be processed w/o signaling
                if(!_hasMoreTasks)
                    Thread.Sleep(5);

                Interlocked.Exchange(ref _status, 0);
            } while (_hasMoreTasks);
        }
    }

    public void Schedule(Action task)
    {
        _hasMoreTasks = true;
        _queue.Enqueue(task);

        SetSignal();
    }

    private void SetSignal()
    {
        if (Interlocked.Exchange(ref _status, 1) == 0)
        {
            _signal.Set();
        }
    }
}

Solution

  • Is it because I use a wait handle that sets when I post a message and releases when the processing is done?

    Without seeing your code it is hard to say for sure, but from your description it appears that the two threads that you wrote act as co-routines: when the main thread is running, the worker thread has nothing to do, and vice versa. It looks like .NET scheduler is smart enough to not load the second core when this happens.

    You can change this behavior in several ways - for example

    • by doing some work on the main thread before waiting on the handle, or
    • by adding more worker threads that would compete for the tasks that your main thread posts, and could both get a task to work on.