I'm new to multithreading, I have a little problem that I can't solve, I would be very grateful if someone could help me...
I'm doing a VERY basic exercise, I'm just trying to simulate the tasks you do at breakfast, like for example, making coffee, heating milk and then making coffee, making some toast, etc... The idea is that there are some tasks that I can do parallel to others. I'm trying to find the most efficient way to do them.
In this example there are 6 tasks:
- Prepare the coffee
- heat the milk
- Prepare the toast
- Prepare the coffee with milk
- Spread the butter on the toast
- Have breakfast
First I can start the tasks of preparing the coffee, heating the milk and preparing the toast.
Once I have finished preparing the coffee and the milk, I can prepare the coffee with milk.
Once I have finished preparing the toast, I can spread the butter on the toast.
Finally, when everything is finished, I can have breakfast.
To do this, I have a simple function that prints a message on the screen with whatever action it is and it takes some time that you pass it as a parameter.
static void task(object P)
{
Parameters p = (Parameters)P;
Console.WriteLine("Starting task: " + p.task);
Thread.Sleep(p.time);
Console.WriteLine("Ending task: " + p.task);
}
The problem is that you don't know what is going to end first, the milk and coffee or the toast. If the milk and coffee are finished first, the action of preparing the coffee with milk will begin, but if the action of preparing the toast is finished first, the action of spreading the butter on the toast will begin. I don't know how to detect this, since I can't do it with joins because I don't know which one will finish first...
I have thought of a solution with an infinite while until one of the two actions is finished but for some reason it doesn't work for me, could someone help me find the error, or explain how to solve this situation?
Thank you very much in advance!
Here is my code:
class Parameters
{
public string? task { get; set; }
public int time { get; set; }
public Parameters(string? task, int time)
{
this.time = time;
this.task = task;
}
}
class Program
{
static Parameters[] p = new Parameters[6];
static void Main(string[] args)
{
p[0] = (new Parameters("Prepare the coffee", 10000));
p[1] = (new Parameters("Heat the milk", 12000));
p[2] = (new Parameters("Prepare the toast", 6000));
p[3] = (new Parameters("Prepare the coffee with milk", 12000));
p[4] = (new Parameters("Spread the butter on the toast", 15000));
p[5] = (new Parameters("Have breakfast", 20000));
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
Thread t3 = new Thread(task);
Thread t4 = new Thread(task);
Thread t5 = new Thread(task);
Thread t6 = new Thread(task);
// Prepare coffee
t1.Start(p[0]);
// Heat the milk
t2.Start(p[1]);
// Prepare the toast
t3.Start(p[2]);
// This is where the error is
bool done1 = false;
bool done2 = false;
while (!done1 && !done2)
{
if (!t1.IsAlive && !t2.IsAlive && !done1)
{
t4.Start(p[3]);
done1 = true;
}
if (!t3.IsAlive && !done2)
{
t5.Start(p[4]);
done2 = true;
}
}
// Joins to have breakfast
t4.Join();
t5.Join();
t6.Start();
}
static void task(object P)
{
Parameters p = (Parameters)P;
Console.WriteLine("Starting task: " + p.task);
Thread.Sleep(p.time);
Console.WriteLine("Ending task: " + p.task);
}
}
Well, to do this with classic threads, one option would be a Semaphore, specifically
SemaphoreSlim;There may be other options that work better, but given this is an 'exercise' I think
SemaphoreSlimwould be a good candidate. Since you have generic parameters, maybe treat the semaphore as a potential dependency in parameters and act on it appropriately.Another option would be AutoResetEvent. Here's some good reading, as part of a free e-book (not written by me, but an invaluable reference!)