C # async: как поток запоминает свои локальные переменные? - PullRequest
0 голосов
/ 01 марта 2019

В многопоточности потоки разделяют несколько вещей, но не локальные переменные.Каждый поток поддерживает свою собственную копию стека, в котором хранятся локальные переменные.Хорошо, но что происходит в этом случае?

public async Task<string> ReadFile()
{
    var fileName = GenerateFileName();

    using (var reader = File.OpenText(fileName))
    {
        var fileText = await reader.ReadToEndAsync();
        return fileName + "|" + fileText;
    }
}

Когда начинается операция ввода-вывода, поток переходит в спящий режим (или выполняет другие действия), а когда он (или другой) возвращается, чтобы продолжить выполнение функциикак он запоминает значение fileName?Что здесь происходит со стеком?

1 Ответ

0 голосов
/ 01 марта 2019

Это происходит аналогично тому, как yield методы хранят свои локальные данные: он создает struct, который имеет локальные переменные в C # в качестве полей.

Таким образом, ваш код создает что-то, чтоесли aysnc не существует, его нужно кодировать следующим образом:

private struct ReadFileAsyncStateMachine : IAsyncStateMachine
{
    public int _state;
    public AsyncTaskMethodBuilder<string> _builder;
    private string _fileName;
    private string _fileText;
    private StreamReader _reader;
    TaskAwaiter<string> _awaiter;
    void IAsyncStateMachine.MoveNext()
    {
        try
        {
            if (_state != 0)
            {
                goto afterSetup;
            }

            _fileName = GenerateFileName();
            _reader = File.OpenText(_fileName);
            TaskAwaiter<string> awaiter = _reader.ReadToEndAsync().GetAwaiter();
            _state = -1;
            if (!awaiter.IsCompleted)
            {
                _awaiter = awaiter;
                _builder.AwaitUnsafeOnCompleted<TaskAwaiter<string>, ReadFileAsyncStateMachine>(ref awaiter, ref this);
                return;
            }

        afterSetup:
            awaiter = _awaiter;
            _fileText = awaiter.GetResult();
            _state = -2;
            _builder.SetResult(_fileName + "|" + _fileText);
            _reader.Dispose();
        }
        catch (Exception exception)
        {
            _state = -2;
            _builder.SetException(exception);
            _reader?.Dispose();
            return;
        }
    }
    [DebuggerHidden]
    void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine param0)
    {
        _builder.SetStateMachine(param0);
    }
}

public Task<string> ReadFile()
{
    ReadFileAsyncStateMachine stateMachine = new ReadFileAsyncStateMachine();
    AsyncTaskMethodBuilder<string> builder = AsyncTaskMethodBuilder<string>.Create();

    stateMachine._builder = builder;
    stateMachine._state = -1;
    builder.Start(ref stateMachine);
    return builder.Task;
}

. ReadFileAsyncStateMachine обрабатывает как возможность немедленного завершения ReadToEndAsync(), и в этом случае возвращаемый Task<string> завершается немедленнои имеет результат или выполняется, и в этом случае возвращаемый Task<string> сам выполняется, и последующий вызов произойдет с MoveNext(), который завершит его.Исключения также обрабатываются.

Как видите, локальные объекты в C # являются полями в реализации.

...