Try the following to see if it meets your needs - it's adapted from here. See the comments in the code for more information.
If anyone has recommendations for improvement, leave them in the comments below.
Create a Windows Forms App (.NET Framework) (name: ConcurrencyTest)
Create a class (name: ControlExtensions.cs)
Note: This code is from here.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConcurrencyTest
{
public static class ControlExtensions
{
public static void Invoke(this System.Windows.Forms.Control control, System.Action action)
{
if (control.InvokeRequired)
control.Invoke(new System.Windows.Forms.MethodInvoker(action), null);
else
action.Invoke();
}
}
}
Create a class (name: Class123EventArgsValueUpdated.cs)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConcurrencyTest
{
public delegate void Class123EventHandlerValueUpdated(object sender, Class123EventArgsValueUpdated e);
public class Class123EventArgsValueUpdated : System.EventArgs
{
public int CurrentManagedThreadId { get; private set; }
public string Status { get; private set; }
public Class123EventArgsValueUpdated(int currentManagedThreadId, string status)
{
CurrentManagedThreadId = currentManagedThreadId;
Status = status;
}
}
}
Create a class (name: Class123.cs)
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ConcurrencyTest
{
public class Class123
{
//event interested parties can register with to know when value is updated.
public event Class123EventHandlerValueUpdated ValueUpdated;
public async Task StartCountAsync(string name, CancellationToken cToken, int delayInMS, int maxVal = 100000)
{
for (int i = 0; i < maxVal; i++)
{
if (cToken.IsCancellationRequested)
cToken.ThrowIfCancellationRequested();
System.Diagnostics.Debug.WriteLine($"{name} [{i}]: {i.ToString()}; Environment.CurrentManagedThreadId: {Environment.CurrentManagedThreadId}");
//if there are subscribers, raise event
ValueUpdated?.Invoke(this, new Class123EventArgsValueUpdated(Environment.CurrentManagedThreadId, $"Reached: {i}"));
await Task.Delay(delayInMS);
}
//if there are subscribers, raise event
ValueUpdated?.Invoke(this, new Class123EventArgsValueUpdated(Environment.CurrentManagedThreadId, "Complete"));
}
}
}
On Form1
add the following:
- 4 Label with the following names:
labelThreadId1
, labelStatus1
, labelThreadId2
, labelStatus2
- 1 Button with the following name:
btnStartStop
Form1.cs:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ConcurrencyTest
{
public partial class Form1 : Form
{
private CancellationTokenSource _tokenSource = null;
public Form1()
{
InitializeComponent();
//set property
btnStartStop.BackColor = Color.LimeGreen;
}
private async Task StartAllCounting()
{
//create new instance
_tokenSource = new CancellationTokenSource();
//create reference
CancellationToken token = _tokenSource.Token;
//create new instance
ConcurrentBag<Task> tasks = new ConcurrentBag<Task>();
//create new instance
Class123 firstInstance = new Class123();
//subscribe to event(s)
firstInstance.ValueUpdated += (s, e) =>
{
//update Labels
if (e.Status != "Complete")
{
labelThreadId1.Invoke(new Action(() => { labelThreadId1.Text = e.CurrentManagedThreadId.ToString(); }));
labelStatus1.Invoke(new Action(() => { labelStatus1.Text = e.Status; }));
}
else
{
labelThreadId1.Invoke(new Action(() => { labelThreadId1.Text = string.Empty; }));
labelStatus1.Invoke(new Action(() => { labelStatus1.Text = "Complete"; }));
}
};
//add
tasks.Add(firstInstance.StartCountAsync("firstInstance", token, 25, 2000));
//create new instance
Class123 secondInstance = new Class123();
//subscribe to event(s)
secondInstance.ValueUpdated += (s, e) =>
{
if (e.Status != "Complete")
{
//update Labels
labelThreadId2.Invoke(new Action(() => { labelThreadId2.Text = e.CurrentManagedThreadId.ToString(); }));
labelStatus2.Invoke(new Action(() => { labelStatus2.Text = e.Status; }));
}
else
{
labelThreadId2.Invoke(new Action(() => { labelThreadId2.Text = string.Empty; }));
labelStatus2.Invoke(new Action(() => { labelStatus2.Text = "Complete"; }));
}
};
//add
tasks.Add(secondInstance.StartCountAsync("secondInstance", token, 1, 2000));
try
{
//wait for all tasks to finish
await Task.WhenAll(tasks.ToArray());
Debug.WriteLine("All tasks completed.");
}
catch (OperationCanceledException ex)
{
Debug.WriteLine($"Info (OperationCanceledException) - {ex.Message}");
//update Labels
labelThreadId1.Invoke(new Action(() => { labelThreadId1.Text = string.Empty; }));
labelStatus1.Invoke(new Action(() => { labelStatus1.Text = "Cancelled by user"; }));
labelThreadId2.Invoke(new Action(() => { labelThreadId2.Text = string.Empty; }));
labelStatus2.Invoke(new Action(() => { labelStatus2.Text = "Cancelled by user"; }));
}
finally
{
_tokenSource.Dispose();
}
// Display status of all tasks.
foreach (Task t in tasks)
System.Diagnostics.Debug.WriteLine("Task {0} status is now {1}", t.Id, t.Status);
}
private async void btnStartStop_Click(object sender, EventArgs e)
{
//if Button is clicked while the method is executing, request cancellation
if (btnStartStop.Text == "Stop")
{
try
{
if (_tokenSource != null)
{
_tokenSource.Cancel();
Debug.WriteLine("Task cancellation requested.");
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
return;
}
//set values
btnStartStop.Text = "Stop";
btnStartStop.BackColor = Color.Red;
await StartAllCounting();
//set values
btnStartStop.Text = "Start";
btnStartStop.BackColor = Color.LimeGreen;
}
}
}
Resources: