Как обмениваться данными между разными потоками, принадлежащими разным классам?

Недавно я читал об async / await и хотел бы знать, как обмениваться данными между разными потоками, принадлежащими к разным классам? Предположим, что у нас есть HttpContext в каком-то веб-приложении. Этот контекст содержит информацию о userId, sessionId и так далее. Наше веб-приложение предоставляет некоторые данные, которые используются некоторым консольным приложением, которое выполняется на другом компьютере. Если в этом консольном приложении возникнут ошибки, я запишу это в файл журнала. userId и sessionId также должны записываться в этот файл журнала. Но каждый поток, созданный в этом консольном приложении, имеет свой собственный контекст. Итак, я ищу способ установить userId и sessionId в контекст потока. Я не хочу использовать какие-то статические классы или volatile поля. Ниже я привел простой пример своего консольного приложения.

    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();
        }
    }

person Joseph Katzman    schedule 13.10.2017    source источник
comment
Потоки не принадлежат классам. В любом случае ваш код не использует потоки, он использует асинхронные методы. Межпоточная синхронизация не требуется - await гарантирует, что выполнение будет продолжено в правильном контексте. Если, конечно, вы не заблокируете его этим searchService.ExecuteSearchAsync().Wait();, что означает, что никакой await не может вернуть   -  person Panagiotis Kanavos    schedule 13.10.2017
comment
Не совсем уверен, что вы пытаетесь сделать, но похоже, что вы можете сделать это, просто передав userId и sessionId в качестве параметров метода.   -  person mikelegg    schedule 13.10.2017
comment
Какой у Вас вопрос? Что ты пытаешься сделать? Вы столкнулись с актуальной проблемой? Единственная проблема в этом коде - public void Execute() и вызов Wait(). Метод должен быть асинхронным   -  person Panagiotis Kanavos    schedule 13.10.2017
comment
Что касается ведения журнала, какой регистратор вы используете? Разные регистраторы используют разные методы для передачи окружающих свойств вне зависимости от контекста потока, точно, потому что он меняется вместе с задачей. Если вы пишете свой собственный регистратор и сталкиваетесь с проблемами, опубликуйте код регистратора   -  person Panagiotis Kanavos    schedule 13.10.2017
comment
@PanagiotisKanavos У меня есть один класс регистратора static, который выполняет запись в файл журнала. Он используется в веб-приложении и консольном приложении. Он берет данные из контекста потока. Это HttpContext в веб-приложении. Но у меня нет userId и sessionId в моем заявлении о соболезновании. Я думаю, что мне следует установить его в контексте для консольного приложения.   -  person Joseph Katzman    schedule 13.10.2017
comment
@JosephKatzman, если у вас проблемы с регистратором, опубликуйте код регистратора. Библиотеки журналирования не в любом случае зависят от контекста потока, потому что он изменяется при использовании задач. Почему бы вам не использовать такие библиотеки, как NLog, Serilog или log4net? Они уже решили все эти проблемы   -  person Panagiotis Kanavos    schedule 13.10.2017
comment
@mikelegg Да, могу, но async/await методов много. По механизму async/await я не знаю, сколько потоков будет создано. Может я буду только одну ветку. Может быть нет.   -  person Joseph Katzman    schedule 13.10.2017
comment
@JosephKatzman - Вы можете не создавать никаких потоков, и все еще может работать асинхронно.   -  person Enigmativity    schedule 13.10.2017


Ответы (1)


Практически никогда не требуется устанавливать данные «в контекст потока», так что выбросьте это из головы.

Хорошо, что вы задумываетесь о безопасности потоков - большинство проблем возникает из-за того, что люди этого не делают. Однако в этом случае нет необходимости по двум причинам:

  1. Судя по тому, что вы говорите, userId и sessionId не меняются в течение жизни этого примера. Многие запросы могут выполняться одновременно, но у каждого стека будет свой идентификатор пользователя / идентификатор сеанса.

  2. Я держу пари, что userid / sessionid являются неизменяемыми типами, то есть не могут быть изменены. Это тот случай, если они являются строками, целыми числами или любым типом значения.

Имея это в виду, вы можете сделать это:

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();
    }
}
person mikelegg    schedule 13.10.2017