0

I was reading about async/await recently and I would like to know how to share data between different threads which belong to different classes? Let's assume that we have the HttpContext in some web application. This context contains information about userId, sessionId and so on. Our web application gives some data which is used by some console application which executed on another computer. If errors are occurred in this console application I will write it to a log file. userId and sessionId also should written to this log file. But each thread created in this console application has its own context. So, I'm looking for a way to set userId and sessionId to thread context. I don't want to use some static classes or volatile fields. I put a simple example of my console application below.

    public sealed class MainService
    {
        /// <summary>
        /// The main method which is called.
        /// </summary>
        public void Execute()
        {
            try
            {
                searchService.ExecuteSearchAsync().Wait();
            }
            catch (Exception e)
            {
                // gets additional info (userId, sessionId) from the thread context
                StaticLoggerClass.LogError(e);
            }
        }
    }

    public sealed class SearchService
    {
        private IRepository  repository = new Repository();

        public async Task ExecuteSearchAsync()
        {
            try
            {
                var result = await this.GetResultsAsync();
            }
            catch (Exception e)
            {
                // gets additional info (userId, sessionId) from the thread context
                StaticLoggerClass.LogError(e);
            }
        }

        private async Task<ResultModel> GetResultsAsync()
        {
            var result = this.repository.GetAsync();
        }
    }

    public sealed class Repository
    {
        private IClient client;

        public async Task<Entity> GetAsync()
        {
            return await client.GetResultAsync();
        }
    }
Joseph Katzman
  • 1,959
  • 6
  • 21
  • 47
  • Threads don't belong to classes. Your code isn't using threads anyway, it uses asynchronous methods. There is no cross-thread synchronization required - await ensures that execution will continue on the correct context. Unless of course you block it with that `searchService.ExecuteSearchAsync().Wait();` which means that no `await` is able to return – Panagiotis Kanavos Oct 13 '17 at 10:56
  • 2
    Not quite sure what you are trying to do, but it sounds like you can do it by just passing the userId and sessionId as method parameters – mikelegg Oct 13 '17 at 10:57
  • What is your question? What are you trying to do? Did you encounter an actual problem? The only problem in this code is `public void Execute()` and the call to `Wait()`. The method should be asynchronous – Panagiotis Kanavos Oct 13 '17 at 10:57
  • As for logging, which logger are you using? Different loggers use different techniques to pass ambient properties without depending on the thread context, *precisely* because that changes with task. If you write your own logger and face issues, post the logger's code – Panagiotis Kanavos Oct 13 '17 at 11:01
  • @PanagiotisKanavos I have one `static` logger class which performs writing to a log file. It is used in web application and console application. It takes the data from thread context. This is `HttpContext` in the web application. But I don't have `userId` and `sessionId` in my condole application. I think that I should to set it to context for console application. – Joseph Katzman Oct 13 '17 at 11:02
  • @JosephKatzman if you have a problem with the logger, post the logger's code. Logging libraries *don't* depend on the thread context anyway, because it changes when using tasks. Why don't you use a library like NLog,Serilog or log4net? They've solved all those problems already – Panagiotis Kanavos Oct 13 '17 at 11:04
  • @mikelegg Yes, I can, but there are a lot of `async/await` methods. According to `async/await` mechanism I don't know how many threads will be created. Maybe I will only one thread. Maybe not. – Joseph Katzman Oct 13 '17 at 11:06
  • @JosephKatzman - You may not get any threads created and things can still run async. – Enigmativity Oct 13 '17 at 11:37

1 Answers1

1

It is almost never necessary to have data 'set to the thread context', so put that out of your head.

It is good that you are considering thread safety - most problems come because people do not. However, in this case there is no need for 2 reasons:

  1. from what you say, userId and sessionId do not change during the life of this example. Many requests might run at the same time, but each stack will have its own userid/sessionid

  2. I'm betting userid/sessionid are immutable types - ie cannot be changed. This is the case if they are strings, or ints, or any value type.

So with that in mind you could do this:

public sealed class MainService
{
    /// <summary>
    /// The main method which is called.
    /// </summary>
    public void Execute()
    {
        try
        {
            // somehow get the userid/sessionid, you don't say where these are from
            var userId = "?";
            var sessionId = "?"

            searchService.ExecuteSearchAsync(userId, sessionId).Wait();
        }
        catch (Exception e)
        {
            // gets additional info (userId, sessionId) from the thread context
            StaticLoggerClass.LogError(e);
        }
    }
}

public sealed class SearchService
{
    private IRepository  repository = new Repository();

    public async Task ExecuteSearchAsync(string userId, string sessionId)
    {
        try
        {
            var result = await this.GetResultsAsync();
        }
        catch (Exception e)
        {
            // gets additional info (userId, sessionId) from the thread context
            StaticLoggerClass.LogError($"userId={userId}; sessionId={sessionId}; error={e}");
        }
    }

    // ........ 
    private async Task<ResultModel> GetResultsAsync()
    {
        var result = this.repository.GetAsync();
    }
}
mikelegg
  • 1,197
  • 6
  • 10