当前位置: 首页 > 知识库问答 >
问题:

C# Winform:为什么这种异步使用不会陷入死锁状态?

石思淼
2023-03-14

我正在调查异步/等待

我有一个问题,在C#winfo:什么是错误的,在这个异步的例子,如果访问结果之前等待上面的问题已经解决。这是一个死锁问题。

但是当我在stackoverflow中搜索一些问题时。我发现了这个:async/wait不同的线程ID

应该也是个僵局问题吧?

我重写了一个类似的代码来测试

    private async void button1_Click(object sender, EventArgs e)
    {
        TB_Log(tbMsg, "click start, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
        Task task = func1();
        TB_Log(tbMsg, "click wait, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
        await task;
        TB_Log(tbMsg, "click end, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
    }

    private async Task func1()
    {
        TB_Log(tbMsg, "func1 start, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
        await Task.Run(() =>
        {
            TB_Log(tbMsg, "func1 task.run, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
            Task<int> task = func2();
            if (task.Result == 5) { ; }
        });
        TB_Log(tbMsg, "func1 end, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
    }

    private async Task<int> func2()
    {
        TB_Log(tbMsg, "func2 start, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
        await Task.Run(() =>
        {
            TB_Log(tbMsg, "func2 task.run,  thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
        });
        TB_Log(tbMsg, "func2 end, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());

        return 1;
    }

代码运行良好,不会阻塞。代码结果显示:

click start, thread id:1
func1 start, thread id:1
click wait, thread id:1
func1 task.run, thread id:3
func2 start, thread id:3
func2 task.run,  thread id:4
func2 end, thread id:4
func1 end, thread id:1
click end, thread id:1

< code >任务。结果不会出现死锁。

我的问题是:

    < li >为什么任务。结果不会造成僵局? < li >为什么func1 end,线程id是1,而不是4或3?

关于2.问题,我打开了一个控制台项目来替换Winform项目。

结果有点不同。

    static async Task Main(string[] args)
    {
        Console.WriteLine("Main Start, thread id:" + Thread.CurrentThread.ManagedThreadId);
        Task task = func1();
        await task;
        Console.WriteLine("Main End, thread id:" + Thread.CurrentThread.ManagedThreadId);
        Console.ReadKey();
    }

    static private async Task func1()
    {
        Console.WriteLine("func1 start, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
        await Task.Run(() =>
        {
            Console.WriteLine("func1 task.run, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
            Task<int> task = func2();
            if (task.Result == 5) {; }
        });
        Console.WriteLine("func1 end, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
    }

    static private async Task<int> func2()
    {
        Console.WriteLine("func2 start, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
        await Task.Run(() =>
        {
            Console.WriteLine("func2 task.run,  thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
        });
        Console.WriteLine("func2 end, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());

        return 1;
    }
Main Start, thread id:1
func1 start, thread id:1
func1 task.run, thread id:4
func2 start, thread id:4
func2 task.run,  thread id:5
func2 end, thread id:5
func1 end, thread id:4
Main End, thread id:4

主要结束id还不是1。

非常感谢你。因为这真的让我感到困惑。

共有1个答案

岳出野
2023-03-14

记住,在你的另一个问题中出现僵局的关键问题是什么?

它在用户界面线程中访问Task.Result(单击处理程序),从而阻塞用户界面线程,任务的延续部分(返回1;也需要在用户界面线程上运行,但它被阻塞了,因此没有蛋糕...

在这个代码示例中,你在UI线程中访问Task.Result吗?没有。所以,没有死锁,吃你的蛋糕...(某些Task.Result的所有直接访问都发生在通过Task.Run执行的一些任务/函数中,因此在线程池的一些后台工作线程中运行,因此不涉及UI线程。)

您在控制台应用程序中观察到的差异与同步上下文有关。简而言之,同步上下文决定了任务的延续(延续是异步函数中跟随等待的任务直到下一个等待的任务或函数结束的代码片段)将在哪个线程上运行。与GUI应用程序相比,控制台应用程序的同步上下文的确定方式不同。您可以在此处阅读所有这些脏衣服:https://devblogs.microsoft.com/pfxteam/await-synchronizationcontext-and-console-apps/

 类似资料:
  • 死锁描述了另外两个线程因为永远等待对方而被阻塞的情况。当死锁发生时,程序永远挂起,你唯一能做的就是杀死程序。 为什么在下面给出的示例生产者-消费者问题中没有发生死锁: 我想知道为什么当同步对象正在等待其他线程释放锁时,在同步块中调用等待方法不会导致死锁?

  • 我试图使用C++11的std::condition_variable,但是当我试图从第二个线程锁定与其关联的unique_lock时,我得到一个异常“资源死锁已避免”。创建它的线程可以锁定和解锁它,但不能锁定第二个线程,即使我非常肯定unique_lock不应该在第二个线程试图锁定它的地方已经锁定。 FWIW我在Linux中使用gcc4.8.1和-std=gnu++11。 我已经围绕conditi

  • 此问题的完整代码可在此处获得:https://github.com/NACHC-CAD/thread-tool 下面显示的代码似乎一直运行到完成,但从未逃脱此处显示的while循环。 如果我取消注释

  • 请问上述代码的第70行换成第71行注释的内容时,为什么会造成死循环。 题目: https://www.acwing.com/problem/content/174/

  • 所以我理解为什么从异步返回空洞通常没有意义,但我遇到了一种我认为完全有效的情况。请考虑以下人为的示例: 我意识到这是一个不寻常的例子,但我试图使其简单化和更普遍化。有人能向我解释为什么这是可怕的代码,以及我如何修改它以正确遵循约定吗? 谢谢你的任何帮助。

  • 然而,在我下面的代码中,我希望在这两个示例中都花费相同的15秒(每个任务5秒),如本文所述。然而,第二个示例只需要5秒,同时运行所有3个示例也需要5秒来完成第二个示例。原来的文章花了5秒,但我把它改成了1秒的延迟,让它更明显。 有没有人能解释一下这是怎么回事,为什么它看起来像线程一样运行?