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: