Skinnix Skinnix - 2 months ago 20
C# Question

C# multiple reusable worker threads

I am trying to build some kind of multi-thread game engine with reusable worker threads. That means the workers are running an endless loop and, after every iteration, call a method that suspends the thread with an

AutoResetEvent
until the next iteration is started from outside. Thet works fine.
But then I want to wait for all of the worker threads to get into the waiting state. But the last thread is always ignored in the first step.
This is my code:

class WorkerThread
{
private readonly Game game;
private readonly Task task;
private readonly AutoResetEvent waitEvent;
private readonly AutoResetEvent finishEvent;

public string Message { get; set; }
public bool Active { get; private set; }

public WorkerThread(Game game)
{
this.game = game;
waitEvent = new AutoResetEvent(false);
finishEvent = new AutoResetEvent(false);
task = new Task(startTask);
}

public void Start()
{
task.Start();
}

public void PerformFrame()
{
finishEvent.Reset();
waitEvent.Set();
}

public void WaitForFrameToFinish()
{
finishEvent.WaitOne();
}

private void startTask()
{
WaitFrame();
run();
}

long sum;
private void run()
{
while (true)
{
sum = 0;
for (int i = 0; i < 1000000; i++)
{
sum += i;
}

Console.WriteLine(Message);
WaitFrame();
}
}

private void WaitFrame()
{
Active = false;
finishEvent.Set();
waitEvent.WaitOne();
Active = true;
}
}

class Game
{
private readonly List<WorkerThread> workers = new List<WorkerThread>();

public Game()
{
workers.Add(new WorkerThread(this) { Message = "Worker 1" });
workers.Add(new WorkerThread(this) { Message = "Worker 2" });
workers.Add(new WorkerThread(this) { Message = "Worker 3" });
workers.Add(new WorkerThread(this) { Message = "Worker 4" });
workers.Add(new WorkerThread(this) { Message = "Worker 5" });
workers.Add(new WorkerThread(this) { Message = "Worker 6" });
}

public void Start()
{
foreach (WorkerThread worker in workers)
{
worker.Start();
}
}

public void OneFrame()
{
//start every worker
foreach (WorkerThread worker in workers)
{
worker.PerformFrame();
}

//wait for the workers to finish
foreach (WorkerThread worker in workers)
{
worker.WaitForFrameToFinish();
}

//check if there are workers still running
foreach (WorkerThread worker in workers)
{
if (worker.Active)
{
Console.WriteLine(worker.Message + " still active");
}
}

Console.WriteLine("Frame finished.\n");
}
}


Has anyone an idea why the
Game
doen't wait for the last
WorkerThread
do finish its task?

EDIT: Using a
ManualResetEventSlim
instead of an
AutoResetEvent
solves the problem. But I still don't understand why the above doesn't work.

EDIT 2: I found the answer and posted it below.

Answer

I just found the solution. startTask uses the WaitFrame method that sets the finishEvent. That way WaitForFrameToFinish will not wait for the worker on the first iteration because finishEvent is already set. If you do it like this, it works fine:

private void startTask()
{
    waitEvent.WaitOne();
    waitEvent.Reset();
    finishEvent.Reset();

    run();
}