异步(async)不是单独开一个线程吗?

6lilu9 2021-04-05 12:04:32
清明充下电,学习下异步与多线程。

窗体上放了一个按钮,一个文本框,经测试,下面的代码是不行的;

private void button1_Click(object sender, EventArgs e)
{
Console.WriteLine($"当前主线程的ID是{Thread.CurrentThread.ManagedThreadId}");
for (int i = 0; i < 100; i++)
{
Console.WriteLine($"当前{i}的ID是{Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(100);
Task.Run(() =>
{
textBox1Set(i);
});
}
Console.WriteLine($"结束了");
}

public void textBox1Set(int I)//供当前及后续类写日志用
{
Console.WriteLine($"当前{I}的textBox1SetID是{Thread.CurrentThread.ManagedThreadId}");
textBox1.Invoke((EventHandler)delegate
{
textBox1.Text = I.ToString();
Console.WriteLine($"当前{I}的textBox1SetID的BeginInvoke是{Thread.CurrentThread.ManagedThreadId}");
});
}


必须用异步方法,加上async 和 await才行,如下:

private async void button1_Click(object sender, EventArgs e)
{
Console.WriteLine($"当前主线程的ID是{Thread.CurrentThread.ManagedThreadId}");
for (int i = 0; i < 100; i++)
{
Console.WriteLine($"当前{i}的ID是{Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(100);
await Task.Run(() =>
{
textBox1Set(i);
});
}
Console.WriteLine($"结束了");
}

public void textBox1Set(int I)//供当前及后续类写日志用
{
Console.WriteLine($"当前{I}的textBox1SetID是{Thread.CurrentThread.ManagedThreadId}");
textBox1.Invoke((EventHandler)delegate
{
textBox1.Text = I.ToString();
Console.WriteLine($"当前{I}的textBox1SetID的BeginInvoke是{Thread.CurrentThread.ManagedThreadId}");
});
}


而我的问题是,用了异步,不就相当于另外开了一个线程吗,那和窗体所在的主线程应该不一样呀,如下图

请高手解惑,谢谢。
...全文
1771 12 打赏 收藏 举报
写回复
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
正怒月神 2021-04-06
  • 打赏
  • 举报
回复
圣殿骑士18 2021-04-06
1、异步 往往(不是一定) 需要多线程配合去完成不同的工作,比如说你这个例子就是用Task实现了多线程。简单的说:异步需要多线程,但异步是不多线程 2、async和await不是多线程,是异步,所以你看到的async包含的代码中,id是一样的,是对的,因为它代码仍然运行在主线程中。 3、只有Task.Run()所包含的代码,才运行在另外一个线程中 4、假设你的代码,使用async await模式能实现10万个并发,但为什么不是实现100万个并发,1000万个并发?是因为仍然有部分代码运行在主线程中,主线程仍然消耗了cpu资源,只不过较少。消耗cpu的,就是你async开始到Task.Run()之间的那部分代码。
  • 打赏
  • 举报
回复
guanyinsishengzi 2021-04-06
2个知识点 1.async await 2.winform invoke/beginvoke 用你的代码解释

private async void button1_Click(object sender, EventArgs e)
        {
           //当前还是同步执行,并没有开始异步 所以线程为UI线程(主线程:线程1)
            Console.WriteLine($"当前主线程的ID是{Thread.CurrentThread.ManagedThreadId}");
            for (int i = 0; i < 100; i++)
            {
               //第一次循环(i=0)时,没有开始任何异步等待(await)所以依然是主线程
                Console.WriteLine($"当前{i}的ID是{Thread.CurrentThread.ManagedThreadId}");

                //第一次循环(i=0)UI线程会阻塞100ms(程序窗口会失去响应100ms),你可以时间改长一点观察以下。
                Thread.Sleep(100);
              
              //此处才开始异步,await机制可以参考两位大神说的,大致就是.net从线程池选择合适的线程执行
              //第二次循环(i=1)时,.net重新执行await。      
              //也就是说从第一次循环的await开始至此方法(button1_Click)结束都是异步 。每次执行至await .net会重新从线程池中选择合适线程。     
               await Task.Run(() => 
                 {
                     textBox1Set(i);
                 });
            }

            Console.WriteLine($"结束了");
        }
 
        public void textBox1Set(int I)//供当前及后续类写日志用
        {
            Console.WriteLine($"当前{I}的textBox1SetID是{Thread.CurrentThread.ManagedThreadId}");

            // textBox1.Invoke意思就是将代码委托至textBox1创建的线程执行,所以一定是UI线程
            //如果使用的时.net framework Winfrom控件的Invoke(Control.Invoke)与委托的Invoke(delegate.Invoke)机制是不同的,
            //相关资料有很多,自己搜。

            textBox1.Invoke((EventHandler)delegate
            {
                textBox1.Text = I.ToString();
                Console.WriteLine($"当前{I}的textBox1SetID的BeginInvoke是{Thread.CurrentThread.ManagedThreadId}");
            });
        }
  • 打赏
  • 举报
回复
wanghui0380 2021-04-05
释放当前线程执行权,去等待另一个IO的完成。 ---------------- 这里的另一个IO的完成,这个另一个IO也许是个线程,也许不是。 多半博大神去给人展示的都是等待另一个线程,因为他们会选择性忘记等待不是另一个线程的情况 比如我上面演示等待另一个按钮触发,比如Iocp去等待网卡去写入数据,比如我们等待File.WaitAsync等待文件驱动去写入文件,比如我们等待底下plc给我一个动作执行完毕操作
  • 打赏
  • 举报
回复
wanghui0380 2021-04-05
我们前面说了,其实他就是IO操作,与线程没关系。他们之所以认为和线程有关系,那是因为那些博大神和相信博大神的重来没想过,一个异步的IO其实和Task没有关系,他实际上倒是和sp1234说的是一个故事,释放当前线程执行权,去等待另一个IO的完成。 这样把,我们写一个不用task的东西看看
  private async void button1_Click(object sender, EventArgs e)
        {
            await semaphore.WaitAsync();
            await WaitButton2Click();
            await semaphore.WaitAsync();
            MessageBox.Show("我等到了button2的点击");
            semaphore.Release();

        }
        System.Threading.SemaphoreSlim semaphore = new SemaphoreSlim(1);
        async Task WaitButton2Click()
        {
            EventHandler action = null;
            action = (object sender1, EventArgs e1) =>
           {
               semaphore.Release();
               button2.Click -= action;
           };
            button2.Click += action;

        }
这里个是一个很简单的演示,点击button,然后用一个异步信号量去暂时释放执行权,等待这个信号量完成,一进行下部的弹框。这例子其实就是告诉那些人不是开启线程,而是等待某个状态完成 ps:那些博大神另一个引以为傲的玩意,所谓IOCP其实就是这种操作。当然那些伙计同样是一贯路子选择性忘记的单向输出。 IOCP其实与C#无关,也和线程无关,他是windows在南桥芯片管理下的IO操作,由windows 网卡驱动,在中断驱动下,直接由DMA操作进行内存写入,写入完成,发起一个IO完成信号。至于他进程也好,线程也罢,其实是就入上面那个代码一样,只是做了一个信号量等待,等待网卡驱动通知你内存写入完成,请继续下面一步 await socket.receiverAsync
  • 打赏
  • 举报
回复
6lilu9 2021-04-05
引用 6 楼 xuzuning 的回复:
异步是多线程! 多线程 中需要 干预线程的行为或结果时一般需要提供一个回调函数(c# 称 委托)这种架构就是异步 async 和 await 是以同步形式是写异步程序的模板
肯定不是多线程呀,线程id都一样的。
  • 打赏
  • 举报
回复
xuzuning 2021-04-05
异步是多线程! 多线程 中需要 干预线程的行为或结果时一般需要提供一个回调函数(c# 称 委托)这种架构就是异步 async 和 await 是以同步形式是写异步程序的模板
  • 打赏
  • 举报
回复
可以运行这个demo:
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine($"开始, thread id = {Thread.CurrentThread.ManagedThreadId}");
            test();
            Console.WriteLine($"主线程test完毕, thread id = {Thread.CurrentThread.ManagedThreadId}");
            Console.WriteLine("按任意键结束........");
            Console.ReadKey();
        }

        private static async void test()
        {
            Console.WriteLine($"test, thread id = {Thread.CurrentThread.ManagedThreadId}");
            var task = Task.Run(async () =>
            {
                int cnt = 0;
                while (cnt++ < 20)
                {
                    await Task.Delay(1000);
                    Console.WriteLine($"while, thread id = {Thread.CurrentThread.ManagedThreadId}");
                }
            });
            await Task.WhenAll(task);   //await语句并未阻塞4的执行
            Console.WriteLine($"while 完成, thread id = {Thread.CurrentThread.ManagedThreadId}");
        }
    }
}
在我这里它输出
开始, thread id = 1
test, thread id = 1
主线程test完毕, thread id = 1
按任意键结束........
while, thread id = 4
while, thread id = 4
while, thread id = 4
while, thread id = 7
while, thread id = 4
while, thread id = 4
while, thread id = 4
while, thread id = 4
while, thread id = 4
while, thread id = 6
while, thread id = 6
while, thread id = 4
while, thread id = 4
while, thread id = 6
while, thread id = 6
while, thread id = 6
while, thread id = 7
while, thread id = 7
while, thread id = 7
while, thread id = 4
while 完成, thread id = 4
可以看到,await 不阻塞线程,同时也能灵活复用和重新分配线程。
  • 打赏
  • 举报
回复
6lilu9 2021-04-05
引用 2 楼 wanghui0380的回复:
从计算机原理上,线程是cpu集中走北桥芯片,使用CPU计算。 IO走南桥,不使用cpu。也就是说,从原理上说等待一个IO事件,原本就不耗cpu,也不用线程。他只是单纯等待一个IO信号 那些博大神告诉你,你可以一边等待电饭煲煮饭,一边去洗菜,切菜。 东西没错,你是可以让CPU去煮饭,只是忘记告诉你。异步等待本身不是开了一个线程,他本身只是等待CPU完成那个煮饭线程后,给你发起一个煮饭完成的IO事件,你实际等待只是这个IO事件,而不是煮饭线程
谢谢,说实话没懂。但至少让我我知道了异步并不是多线程。
  • 打赏
  • 举报
回复
Task 是对系统线程池的良好封装,而且也是各种语言都支持的标准“语言”。例如你需要100万个相对独立的任务,但是实际上由于各种原因,其实进程中只要复用15个子线程就足够了,这就只要使用简洁的 Task 概念来编程,你“认为”逻辑上需要10万个线程,而实际上只动用15个线程。所以并不是说异步声明的不同部分的代码(方法)一定运行在不同线程。 此Task不是操作系统概念,而是.net 中的机制。 “异步”是一种逻辑概念,使用“声明的形式”将输入输出分离开成为两个部份。你可以认为它是一切“回调、事件”的原型概念。 async/await 是一种实现异步声明的语法,这样c#编译器就知道如何编写线程池代码。 “await 某种Task对象”这样的代码并不是阻塞线程,而是释放当前线程。这就是异步编程——使用async/await实现——跟过去的使用线程阻塞的编程方式的区别。当你使用阻塞的思维方式,那么你要想干100万个独立任务,那么你真的就得创建100万个线程了,那需要1千核CPU且100T内存的未来世界计算机。而Task则是轻量级的。
  • 打赏
  • 举报
回复
wanghui0380 2021-04-05
从计算机原理上,线程是cpu集中走北桥芯片,使用CPU计算。 IO走南桥,不使用cpu。也就是说,从原理上说等待一个IO事件,原本就不耗cpu,也不用线程。他只是单纯等待一个IO信号 那些博大神告诉你,你可以一边等待电饭煲煮饭,一边去洗菜,切菜。 东西没错,你是可以让CPU去煮饭,只是忘记告诉你。异步等待本身不是开了一个线程,他本身只是等待CPU完成那个煮饭线程后,给你发起一个煮饭完成的IO事件,你实际等待只是这个IO事件,而不是煮饭线程
  • 打赏
  • 举报
回复
wanghui0380 2021-04-05
那些博大神,最喜欢干的事是单边忘记式输出。 他们忘记告诉你的事情是async/await的全称叫做 异步IO及等待 所以他跟线程没有任何关系,只是他们在单边输出的时候喜欢拽上task,所以在跟线程挂上了边。因为task才是线程的升级,async并不是。async只是异步IO的升级封装
  • 打赏
  • 举报
回复
发帖
C#

10.8w+

社区成员

.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
帖子事件
创建了帖子
2021-04-05 12:04
社区公告

让您成为最强悍的C#开发者