Reputation: 41
My goal is to create a system that spawns an initial number of tasks(all the same). These tasks will grab and perform some background operation (from a DB) and then return, at which point a new task should spawn and do the same thing.
I have written the following code as a proof of concept, but it seems like all my tasks are executing 1 by 1, not in parallel.
Code:
public Form1()
{
InitializeComponent();
}
CancellationTokenSource cts;
private async void button1_Click(object sender, EventArgs e)
{
cts = new CancellationTokenSource();
int reqNumberOfThreads = int.Parse(textBox1.Text);
try
{
await startSlaves(cts.Token, reqNumberOfThreads);
}
catch (OperationCanceledException)
{
MessageBox.Show("Canceled Starting Threads");
}
cts = null;
}
async Task startSlaves(CancellationToken ct, int threadNum)
{
List<Task<int>> allTasks = new List<Task<int>>();// ***Add a loop to process the tasks one at a time until none remain.
for (int x = 0; x < threadNum; x++)
{
allTasks.Add(beginSlaveOperation(ct, x));
}
// ***Add a loop to process the tasks one at a time until none remain.
while (allTasks.Count <= threadNum)
{
// Identify the first task that completes.
Task<int> output = await Task.WhenAny(allTasks);
allTasks.Remove(output);
allTasks.Add(beginSlaveOperation(ct, output.Result));
}
}
public void performExampleImportOperationThread(int inputVal, int whoAmI)
{
System.Threading.Thread.Sleep(inputVal*10);
System.Console.Write("Thread number" + whoAmI.ToString() + "has finished after "+inputVal.ToString()+" secs \n");
}
async Task<int> beginSlaveOperation(CancellationToken ct, int whoAmI)
{
Random random = new Random();
int randomNumber = random.Next(0, 100);//Get command from microSched and remove it from sched
performExampleImportOperationThread(randomNumber, whoAmI);//perform operation
return whoAmI;
}
private void button2_Click(object sender, EventArgs e)
{
if (cts != null)
{
cts.Cancel();
}
}
Output:
Thread number0has finished after 29 secs Thread number1has finished after 45 secs Thread number2has finished after 59 secs Thread number0has finished after 39 secs Thread number1has finished after 13 secs Thread number2has finished after 44 secs Thread number0has finished after 21 secs Thread number1has finished after 62 secs Thread number2has finished after 62 secs Thread number0has finished after 25 secs Thread number1has finished after 86 secs Thread number2has finished after 10 secs Thread number0has finished after 4 secs Thread number1has finished after 24 secs Thread number2has finished after 84 secs Thread number0has finished after 73 secs Thread number1has finished after 19 secs Thread number2has finished after 72 secs Thread number0has finished after 82 secs
Upvotes: 1
Views: 2480
Reputation: 41
Here's the fixed code for future reference:
public Form1()
{
InitializeComponent();
}
CancellationTokenSource cts;
private async void button1_Click(object sender, EventArgs e)
{
cts = new CancellationTokenSource();
int reqNumberOfThreads = int.Parse(textBox1.Text);
try
{
await startSlaves(cts.Token, reqNumberOfThreads);
}
catch (OperationCanceledException)
{
MessageBox.Show("Canceled Starting Threads");
}
cts = null;
}
async Task startSlaves(CancellationToken ct, int threadNum)
{
List<Task<int>> allTasks = new List<Task<int>>();// ***Add a loop to process the tasks one at a time until none remain.
for (int x = 0; x < threadNum; x++)
{
allTasks.Add(beginSlaveOperation(ct, x));
}
// ***Add a loop to process the tasks one at a time until none remain.
while (allTasks.Count <= threadNum)
{
// Identify the first task that completes.
Task<int> output = await Task.WhenAny(allTasks);
allTasks.Remove(output);
allTasks.Add(beginSlaveOperation(ct, output.Result));
}
}
public async Task performExampleImportOperationThread(int inputVal, int whoAmI)
{
await Task.Delay(inputVal*100);
System.Console.Write("Thread number" + whoAmI.ToString() + "has finished after "+inputVal.ToString()+" secs \n");
}
async Task<int> beginSlaveOperation(CancellationToken ct, int whoAmI)
{
Random random = new Random();
int randomNumber = random.Next(0, 100);//Get command from microSched and remove it from sched
await performExampleImportOperationThread(randomNumber, whoAmI);//perform operation
return whoAmI;
}
private void button2_Click(object sender, EventArgs e)
{
if (cts != null)
{
cts.Cancel();
}
}
Upvotes: 0
Reputation: 203829
Your beginSlaveOperation
is marked as async
, and you call it as if it were asynchronous, but you never actually await
anything, so it is going to run synchronously. (Which makes its caller run synchronously, and its caller run synchronously, and so on, so that your entire application is running synchronously.)
Marking a method async
doesn't magically make it run in a new thread. All it does is let you use the await
keyword. If you don't ever use it, all you've done is execute a bunch of code synchronously and wrap the result in a completed task. You should even get a compiler warning for doing this.
You can fix that by having performExampleImportOperationThread
be an async
method and, instead of using Thread.Sleep(...)
, use await Task.Delay(...)
. That await performExampleImportOperationThread
to make beginSlaveOperation
actually be asynchronous.
Or you could just not do everything that you're doing and replace it all with a call to Parallel.For
, which can set a MaxDegreesOfParallelism
to limit you to a specific number of concurrent threads.
Upvotes: 1