Babu James
Babu James

Reputation: 2843

C# multithreading combined with async/await

This is yet another question related to multi-threading in C#.

However, when multi-threading combined with async/await seems to be interesting.

I have a method generating threads which invoke methods of objects asynchronously.

However, the context is not shared by objects of different classes.

For example, consider the code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ThreadDataSharing
{
    class Data
    {
        public static ThreadLocal<string> Name = new ThreadLocal<string>();
    }

    class Locker { public static object lobj = new object { };}
    class Program2
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 3; i++)
            {
                var pObj = new Program();
                new Thread(new ThreadStart(pObj.MyThreadMethod)).Start();
            }
        }
    }
    class Program
    {

        ///my async method
        ///
        public async Task MyAsyncMethod()
        {
            lock (Locker.lobj)
            {
                Console.WriteLine("From async method");
                Console.WriteLine("thread: " + Thread.CurrentThread.ManagedThreadId);
                Console.WriteLine("id: " + Data.Name.Value);
            }

            await MyAsyncMethod2();

            await new AsyncClass().AsyncMethod();
        }

        public async Task MyAsyncMethod2()
        {
            lock (Locker.lobj)
            {
                Console.WriteLine("From async 2 method");
                Console.WriteLine("thread: " + Thread.CurrentThread.ManagedThreadId);
                Console.WriteLine("id: " + Data.Name.Value);
            }

            await Task.Run(() =>
            {
                lock (Locker.lobj)
                {
                    Console.WriteLine("From task run method");
                    Console.WriteLine("thread: " + Thread.CurrentThread.ManagedThreadId);
                    Console.WriteLine("id: " + Data.Name.Value);
                }
            });
        }

        public void MyThreadMethod()
        {
            lock (Locker.lobj)
            {
                Console.WriteLine("From thread method");
                Console.WriteLine("thread: " + Thread.CurrentThread.ManagedThreadId);

                var id = Guid.NewGuid();
                Data.Name.Value = id.ToString();
                Console.WriteLine("id: " + Data.Name.Value);
            }

            MyAsyncMethod().Wait();
        }
    }


    public class AsyncClass
    {
        public async Task AsyncMethod()
        {
            lock (Locker.lobj)
            {
                Console.WriteLine("From class async method");
                Console.WriteLine("thread: " + Thread.CurrentThread.ManagedThreadId);
                Console.WriteLine("id: " + Data.Name.Value);
            }

            var newId = Guid.NewGuid();
            Data.Name.Value = newId.ToString();

            await AsyncMethod2();
        }

        public async Task AsyncMethod2()
        {
            lock (Locker.lobj)
            {
                Console.WriteLine("From class async 2 method");
                Console.WriteLine("thread: " + Thread.CurrentThread.ManagedThreadId);
                Console.WriteLine("id: " + Data.Name.Value);
            }

            await Task.Run(() => { });
        }
    }

}

Running this code produces the following output on my machine

From thread method
thread: 3
id: 3a7ef8e3-ebe1-49e1-94bb-798358ac1567
From thread method
thread: 4
id: 341d3371-f905-4e1c-aac3-47bda16c8d88
From thread method
thread: 5
id: b7e79901-81b7-430f-b158-59f091e43a0c
From async method
thread: 3
id: 3a7ef8e3-ebe1-49e1-94bb-798358ac1567
From async method
thread: 5
id: b7e79901-81b7-430f-b158-59f091e43a0c
From async method
thread: 4
id: 341d3371-f905-4e1c-aac3-47bda16c8d88
From async 2 method
thread: 3
id: 3a7ef8e3-ebe1-49e1-94bb-798358ac1567
From async 2 method
thread: 4
id: 341d3371-f905-4e1c-aac3-47bda16c8d88
From async 2 method
thread: 5
id: b7e79901-81b7-430f-b158-59f091e43a0c
From task run method
thread: 6
id:
From task run method
thread: 8
id:
From task run method
thread: 7
id:
From class async method
thread: 7
id:
From class async method
thread: 6
id:
From class async method
thread: 8
id:
From class async 2 method
thread: 8
id: f52ed654-0e55-4906-bfc1-65c6b25a7785
From class async 2 method
thread: 7
id: 1e53e03b-a3a0-4296-8622-7716b45d1462
From class async 2 method
thread: 6
id: 1adca81d-b11a-4860-b37d-a017afe877b8

Question is, why is it not sharing the ThreadLocal<T> object with the instance of the second Class (AsyncClass)?

Upvotes: 2

Views: 1680

Answers (2)

Stephen Cleary
Stephen Cleary

Reputation: 457187

the context is not shared by objects of different classes.

Actually, the behavior you're seeing is that the (thread-local) context is not shared between threads.

I have an async intro blog post that may clear up this behavior. The essential concept is that async methods do not resume on the same thread; rather, they resume on a captured context (by default). In this case, the thread pool context is used to resume your async methods, which means that those continuations are just scheduled to the thread pool, and may be run by any thread pool thread.

Upvotes: 2

Marc Cals
Marc Cals

Reputation: 2989

In a Console app with await/async there isn't shared synchronization context, may be this is the reason why it's not shared.

Upvotes: 3

Related Questions