Home > front end >  A test on threads
A test on threads

Time:10-18

I wrote a thread test application to see multiple and parallel started threads returning behaviour. I am new on async programming so can you please give a comment about this code?

I create multiple instances of TestThreadClass using Parallel to see what is happening when async operation is completed at the same time. When threads completed, I added them into a thread-safe dictionary (ConcurrentDictionary) with their random generated guids.

Below is a random async class returns in 1 milliseconds;

public class TestThreadClass
{
    private Task<ThreadResultModel> myTask;
    public readonly int myNumber;
    public readonly string myId;

    public TestThreadClass(int _myNumber)
    {
        myNumber = _myNumber;

        myId = Guid.NewGuid().ToString();
    }

    public async Task<ThreadResultModel> StartOperation()
    {
        myTask = InvokeOperation();

        return await myTask;
    }

    private async Task<ThreadResultModel> InvokeOperation()
    {
        await Task.Delay(TimeSpan.FromMilliseconds(1));

        return new ThreadResultModel(myNumber, myId, "Returned");
    }
}

Below is creator class I used WinForms application. I start parallel threads and after they completed I fill them to a GridView to compare return milliseconds;

public partial class Form1 : Form
{
    private const int threadNumber = 100;
    private readonly ConcurrentDictionary<string, ThreadResultModel> startedConcurrentDictionary;
    private readonly ConcurrentDictionary<string, ThreadResultModel> returnedConcurrentDictionary;

    public Form1()
    {
       InitializeComponent();

       startedConcurrentDictionary = new ConcurrentDictionary<string, ThreadResultModel>();
       returnedConcurrentDictionary = new ConcurrentDictionary<string, ThreadResultModel>();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
       FillComboboxes();
    }

    private void FillComboboxes()
    {
        for (int i = 1; i <= threadNumber; i  )
        {
           DdlThreadNumber.Items.Add(i.ToString());
        }
    }

    private void BtnStartThreads_Click(object sender, EventArgs e)
    {
        Parallel.For(0, int.Parse(DdlThreadNumber.Text), StartAThread);
    }

    private void StartAThread(int threadTag)
    {
        TestThreadClass t = new TestThreadClass(threadTag);

        startedConcurrentDictionary.TryAdd(t.myId, new ThreadResultModel(threadTag, t.myId, "Started"));
            
       t.StartOperation().ContinueWith(result =>
       {
           returnedConcurrentDictionary.TryAdd(result.Result.MyId, result.Result);
       });
    }

    private void BtnReport_Click(object sender, EventArgs e)
    {
       foreach (var item in startedConcurrentDictionary)
       {
            GrdThreads.Rows.Add(item.Value.MyNumber, item.Value.MyId, item.Value.EventType, item.Value.Time);
       }

       foreach (var item in returnedConcurrentDictionary)
       {
           GrdThreads.Rows.Add(item.Value.MyNumber, item.Value.MyId, item.Value.EventType, item.Value.Time);
       }
    }

    private void GrdThreads_SelectionChanged(object sender, EventArgs e)
    {
        statusLabel.Text = GrdThreads.SelectedRows.Count.ToString();
    }
}

I just want to know is this approach is correct or not.

CodePudding user response:

I just want to know is this approach is correct or not.

The answer to this question depends on what you are trying to achieve. What the code above does is:

  1. Parallel.For executes several items at once, but not all. It runs 100 calls on underlying scheduler and the number of executions at once depends on number of CPU cores. But it won't start new threads for each call, just re-use existing ones
  2. Each StartAThread doesn't start a thread. They are just run on Parallel.For scheduler's thread until the await. After that, they disappear from any thread and after some delay they are added to ThreadPool
  3. ThreadPool executes the work after awaits, load-balancing the calls between its threads
  4. Code in ContinueWith method is also added on ThreadPool

So, by just using await or Parallel.For, you won't create 100 threads. Since creating threads is slow and each thread take much memory, all those features will use some internal mechanisms to re-use the existing threads, as you don't need more threads than CPU can handle

But you still create 100 concurrent Tasks, all split into several parts (with await or ContinueWith), run independently by ThreadPool on several threads (usually about number of CPU cores/threads).

If you literally want to create 100 threads, you need to use new Thread(...) call

  • Related