Задача запускается в контексте пользовательского интерфейса - PullRequest
2 голосов
/ 20 марта 2019

У меня есть этот кусок кода для асинхронного выполнения «тяжелой» задачи (и, возможно, отображения результата в пользовательском интерфейсе). В моем понимании, новая задача всегда будет начинаться в другом контексте, чем пользовательский интерфейс. Чтобы проверить это, я написал следующий код:

private async void button2_Click(object sender, EventArgs e)
{
    label2.Text = Thread.CurrentContext.ContextID.ToString();
    await Task.Run(() =>
    {
        Thread.Sleep(2000);
        label2.Text = Thread.CurrentContext.ContextID.ToString();
        Thread.Sleep(2000);
    });

    label2.Text = Thread.CurrentContext.ContextID.ToString();
}

Я думал, что Задача (запущенная в другом контексте) не может изменять значения в контексте пользовательского интерфейса. Здесь это делается строкой label2.Text = Thread.CurrentContext.ContextID.ToString(); внутри Задачи. И это работает без нареканий. Также Thread.CurrentContext.ContextID одинаково внутри Задачи и снаружи. Я также проверил текущий поток (не показан в коде), и он отличался внутри задачи.

Я думаю, что мое понимание задач неверно. Почему эта задача запускается в том же контексте, что и пользовательский интерфейс, даже если она выполняется в другом потоке?

1 Ответ

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

Из некоторого базового тестирования выясняется, что единственная причина, по которой вы не получаете исключение кросс-потока, заключается в том, что текст, который вы поместили в label2 из другого потока, соответствует уже существующему тексту, поэтому обновление в моем случае не требуется Thread.CurrentContext.ContextID было 0.

Например:

private async void button2_Click(object sender, EventArgs e)
{
    label2.Text = "Null";
    await Task.Run(() =>
    {
        Thread.Sleep(2000);
        label2.Text = SynchronizationContext.Current?.ToString() ?? "Null";
        Thread.Sleep(2000);
    });

    label2.Text = "Done";
}

Однако этот код выполняется без исключения, если мы изменим текст:

private async void button2_Click(object sender, EventArgs e)
{
    label2.Text = SynchronizationContext.Current.ToString(); //WinForms Sync Context
    await Task.Run(() =>
    {
        Thread.Sleep(2000);
        label2.Text = SynchronizationContext.Current?.ToString() ?? "Null";
        Thread.Sleep(2000);
    });

    label2.Text = "Done";
}

Теперь мы получим перекрестную нить, InvalidOperationException, как и ожидалось. Поэтому, если мы не изменим значение компонента потока пользовательского интерфейса, исключение не произойдет, если мы изменим значение, мы получим ожидаемое исключение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...