简体   繁体   English

为什么异步 Task.Run 会终止 Thread 而 Task Run 不会?

[英]Why does an async Task.Run terminate a Thread and a Task Run does not?

I am starting a Thread where an await Task.Run can be invoked.我正在启动一个可以调用await Task.Run的线程。

After starting a Thread with the ThreadStart.Start method, why does the await Task.Run terminate the Thread and Task.Run does not?使用ThreadStart.Start方法启动 Thread 后,为什么await Task.Run终止 Thread 而Task.Run却没有?

Here is some code as an example:以下是一些代码作为示例:

public async Task Task1()
{
    if (awaitRunTask)
    {
        await Task.Run(async () =>
        {
            await Test();
        }
        );
    }
    else
    {
        Task.Run(async () =>
        {
            await Test();
        }
        );
    }
}

In the above example, if a Thread invokes the Task1 method, and awaitRunTask = true , the Thread terminates.在上面的示例中,如果 Thread 调用 Task1 方法,并且awaitRunTask = true ,则 Thread 终止。 If awaitRunTask = false , the Thread does not terminate.如果awaitRunTask = false ,线程不会终止。

When I say terminate, the Thread does not complete correctly and the method where the ThreadStart.Start is invoked returns.当我说终止时,线程没有正确完成并且调用ThreadStart.Start的方法返回。 This happens at the await Test() code.这发生在await Test()代码中。

Why is this and if I want to await a Task.Run on a Thread, is there a way to do this?为什么会这样,如果我想在线程上等待Task.Run ,有没有办法做到这一点?

EDIT编辑

Here is some code to show a more detailed example:这是一些代码来显示更详细的示例:

public class ThreadExample
{
    public bool awaitRunTask;
    public string value;
    private async Task StartThreadAsync()
    {
        var method = this.GetType().GetMethod("RunTasksAsync");
        ThreadStart threadStart;
        threadStart = async () =>
        {
            await InvokeAsync(method, this, null);
        };
        var thread = new Thread(threadStart);
        thread.Start();
        thread.Join();
    }
    public async Task RunTasksAsync()
    {
        await Task1Async();
        Task2();
    }
    private async Task Task1Async()
    {
        if (awaitRunTask)
        {
            await Task.Run(async () =>
            {
                await TestAsync();
            }
            );
        }
        else
        {
            Task.Run(async () =>
            {
                await TestAsync();
            }
            );
        }
    }
    private void Task2()
    {
        value = "valid";
    }
    private async Task TestAsync()
    {
        await Task.Delay(1000);
    }
    private async Task InvokeAsync(MethodInfo method, object instance, object[] parameters)
    {
        dynamic awaitable = method.Invoke(instance, parameters);
        await awaitable;
    }
    public async Task ValueIsCorrectAsync()
    {
        value = "not valid";
        awaitRunTask = false;
        await StartThreadAsync();
        var isCorrect = (value == "valid");
    }
    public async Task ValueIsNotCorrectAsync()
    {
        value = "not valid";
        awaitRunTask = true;
        await StartThreadAsync();
        var isCorrect = (value == "valid");
    }
}

The ValueIsCorrectAsync method works correctly as the Task2 method sets the value field.Task2方法设置value字段时, ValueIsCorrectAsync方法可以正常工作。

The ValueIsNotCorrectAsync method does not work correctly as the await Task.Run in the Task1Async method interferes with the Thread. ValueIsNotCorrectAsync方法无法正常工作,因为Task1Async方法中的await Task.Run会干扰 Thread。 The StartThreadAsync method returns before the Task2 method sets the value field. StartThreadAsync方法在Task2方法设置value字段之前返回。

The only difference between the two methods, is the value of awaitRunTask .这两种方法的唯一区别是awaitRunTask的值。

How should I change my code such that the value field is set correctly when awaitRunTask = true ?我应该如何更改我的代码,以便在awaitRunTask = true时正确设置value字段?

EDIT3编辑3

If the await Task.Delay(1000);如果await Task.Delay(1000); is commented out in the TestAsync method, the code works for both awaitRunTask = true and awaitRunTask = false;TestAsync方法中被注释掉,代码适用于awaitRunTask = trueawaitRunTask = false;

Can someone please explain to me why?有人可以向我解释为什么吗? I need to know why because the TestAsync method needs to be able to run asynchronous code.我需要知道为什么,因为TestAsync方法需要能够运行异步代码。

Why is this?为什么是这样?

As I explain on my blog, await here is actually returning to its caller .正如我在我的博客上解释的那样, await这里实际上是返回到它的 caller So the Task1 method returns an incomplete task to its caller, presumably the thread's main method, which presumably is async void .所以Task1方法返回一个不完整的任务给它的调用者,大概是线程的 main 方法,大概是async void When the thread's main method returns (due to it's await ), the thread exits.当线程的 main 方法返回时(由于它是await ),线程退出。

The core of the problem is that the Thread type doesn't understand or work naturally with asynchronous code.问题的核心是Thread类型不能理解或自然地与异步代码一起工作。 Thread is really a very low-level building block at this point and is best avoided in modern code.在这一点上, Thread实际上是一个非常低级的构建块,最好在现代代码中避免使用。 There are very few scenarios where it can't be replaced with Task.Run .很少有场景不能用Task.Run代替。

if I want to await a Task.Run on a Thread, is there a way to do this?如果我想在线程上等待 Task.Run,有没有办法做到这一点?

The easiest solution is to get rid of the legacy thread completely;最简单的解决方案是彻底摆脱遗留线程; replace it with Task.Run .将其替换为Task.Run

Otherwise, you need the thread to block.否则,您需要线程阻塞。 If the continuations can run on thread pool threads, then you can just block directly (eg, GetAwaiter().GetResult() ).如果延续可以在线程池线程上运行,那么您可以直接阻塞(例如GetAwaiter().GetResult() )。 If the continuations need to run on that thread, then use AsyncContext from my AsyncEx library .如果需要在该线程上运行延续,则使用我的AsyncEx library中的AsyncContext

Here's a minimal version of your sample code:这是您的示例代码的最小版本:

async Task Main()
{
    var te = new ThreadExample();
    await te.StartThreadAsync(false);
    await te.StartThreadAsync(true);
}

public class ThreadExample
{
    public string value;
    public async Task StartThreadAsync(bool awaitRunTask)
    {
        value = "not valid";
        var thread = new Thread(() => Task1Async(awaitRunTask));
        thread.Start();
        thread.Join();
        var isCorrect = (value == "valid");
        Console.WriteLine(isCorrect);
    }

    private async Task Task1Async(bool awaitRunTask)
    {
        if (awaitRunTask)
        {
            await Task.Run(async () => await Task.Delay(1000));
        }
        value = "valid";
    }
}

This outputs:这输出:

True
False

The thread that enters Task1Async executes the line value = "valid" when awaitRunTask == false , but when it's true it hits the await Task.Run and, because of the async state machine, the thread returns to the caller at this point and executes the thread.Join() .进入Task1Async的线程在awaitRunTask == false时执行行value = "valid" ,但当它为true时,它会命中await Task.Run并且由于async state 机器,线程此时返回到调用者并执行thread.Join()

Effectively you've created an extreme race condition.实际上,您已经创造了一个极端的比赛条件。

I think because when you do我想因为当你这样做时

if (true)
    {
        await Task.Run(async () =>
        {
            await Test();
        }
        );
    }

The thread waits until the task run completes because of await , whereas when you do由于await ,线程一直等到任务运行完成,而当你这样做时

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

A task is run on a thread (which may be the current thread), but your code does not wait until the task completes.任务在线程(可能是当前线程)上运行,但您的代码不会等到任务完成。 That may explain why the thread does not finish.这可以解释为什么线程没有完成。 Hope this helps.希望这可以帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM