异步调用与线程(总结篇)

委托调用、子线程程调用、与线程池调用
1,委托调用
(1),同步委托:委托的Invoke方法用来进行同步调用。同步调用也可以叫阻塞调用,它将阻塞当前线程,然后执行调用,调用完毕后再继续向下进行。
 从下面的例子中可以看到,同步委托的执行是在主线程main中执行的,所以当执行委托时,当前工作会处于等待状态,开始执行委托,当委托执行完后在继续执行“当前工作”

public delegate int AddHandler(int i,int y);
        private void button1_Click(object sender, EventArgs e)
        {
           //添加当前主线程名称“main”
            Thread.CurrentThread.Name = "main";
            AddHandler handler = new AddHandler(Add);
            Debug.WriteLine(handler.Invoke(1,2));
            Debug.WriteLine("OK");
        }
        int Add(int x,int y)
        {
           //输出当前执行操作的现场
            Debug.WriteLine(Thread.CurrentThread.Name);
           return x + y;
        }

输出结果:main
               OK
 从Debug.WriteLine(Thread.CurrentThread.Name) 看出同步委托代码执行所在的线程与调用方式相关,同步委托代码执行所在的线程等于调用委托所在的线程.
(2),异步委托:异步调用不阻塞主线程,而是把调用在线程池中的新线程中执行,我们可以不必关心,也无需关心这个“新线程”是怎么定义的
     委托的异步调用通过BeginInvoke和EndInvoke来实现。
 从下面的例子中可以看到,异步委托的执行是在新线程中执行,所以当执行委托时,当前工作会不会阻塞,异步委托 与当前线程是同时执行的。

public delegate int AddHandler(int i,int y);
        private void button1_Click(object sender, EventArgs e)
        {
            //添加当前主线程名称“main”
              Thread.CurrentThread.Name = "main";
            AddHandler handler = new AddHandler(Add);
            Debug.WriteLine(handler.BeginInvoke(1, 2, null, null));
            Debug.WriteLine("OK");
        }
        int Add(int x,int y)
        {
           //输出当前执行操作的现场
            Debug.WriteLine(Thread.CurrentThread.Name);
           return x + y;
        }

输出结果:OK
               空   
 Debug.WriteLine(Thread.CurrentThread.Name) 输出为空,看出异步委托代码执行是在我们没有指定名字的新线程中执行的。
 备注:由于异步委托时启用线程池线程执行,.Net没有赋予程序员直接停止其调用的方法,使得我们没有办法直接控制委托的停止和执行,假设 Add是一个0-100循环,一般情况下我们是没有办法 在委托循环到50让委托停下来的,二般情况是可以通过一些特殊的手段的需要的话就Goolge一下吧!所以显得委托调用不够灵活

下面给一个一步委托返回值的列子:

public delegate int AddHandler(int i,int y);
        private void button1_Click(object sender, EventArgs e)
        {
            //添加当前主线程名称“main”
              Thread.CurrentThread.Name = "main";
            AddHandler handler = new AddHandler(Add);
            IAsyncResult obj = handler.BeginInvoke(1, 2, null, null);
            //使用EndInvoke方法接收返回值
            int i = handler.EndInvoke(obj);            
            Debug.WriteLine(i.ToString());
        }
        int Add(int x,int y)
        {
           //输出当前执行操作的现场
            Debug.WriteLine(Thread.CurrentThread.Name);
           return x + y;
        }

2,子线程调用:子线程的最大特点是在子线程执行任务时候,不占用主线程,而且我们可以自由控制它。Visual C#中使用的线程都是通过自命名空间System.Threading中的Thread类经常实例化完成的。通过Thread类的构造函数来创建可供Visual C#使用的线程,通过Thread中的方法和属性来设定线程属性和控制线程的状态。以下Thread类中的最典型的构造函数语法,在Visual C#中一般使用这个构造函数来创建、初始化Thread实例。

private void button1_Click(object sender, EventArgs e)
        {
            //添加当前主线程名称“main”
            Thread.CurrentThread.Name = "main";

//通过Thread类的构造函数线程,并指示一个委托让线程 执行指定方法
            Thread t = new Thread(new ThreadStart(Add));
            t.Name = "子线程";
            //开始新线程
            t.Start();
            
            Debug.WriteLine(Thread.CurrentThread.Name);
        }

void Add()
        {
            for (int i = 0; i < 100000; i++)
            {
                //输出当前执行操作的线程名
                Debug.WriteLine(Thread.CurrentThread.Name+i);

}
        }

输出结果:
        main
        子线程0
        子线程2
        子线程3
        从输出结果我们 可以看到 ,新线程的执行,不会阻塞主线程。 我们可以通过 Abort()方法结束线程。这里就不给出代码了。
        下面说一下,带参数的线程委托,看下面的代码:

void Add(int q)
        {
            for (int i = 0; i < q; i++)
            {
                //输出当前执行操作的线程名
                Debug.WriteLine(Thread.CurrentThread.Name + i);

}
        }
        private void button1_Click(object sender, EventArgs e)
        {
    
            Thread.CurrentThread.Name = "main";
            Thread t = new Thread(new ThreadStart(Add(100)));
            t.Name = "子线程";
    
            t.Start();

Debug.WriteLine(Thread.CurrentThread.Name);
        }

//如果你像这样 Thread t = new Thread(Add(100));传入参数的话肯定是不可能的 因为委托时不能带参数的,这里提供一种简单的解决方法,就是在线程委托中再委托的办法实现

private void button1_Click(object sender, EventArgs e)
        {
            //添加当前主线程名称“main”
            Thread.CurrentThread.Name = "main";
            //在线程委托中再定义一个委托在新委托中调用方法void Add(int q)。
            Thread t = new Thread(new ThreadStart(delegate {Add(1000); }));
            t.Name = "子线程";
            //开始新线程
            t.Start();
            Debug.WriteLine(Thread.CurrentThread.Name);
        }

带返回值的:

private void button1_Click(object sender, EventArgs e)
        {
            //添加当前主线程名称“main”
            Thread.CurrentThread.Name = "main";
            //定义一个变量准备接收子线程返回值
            int iResult = 0;
            //在线程委托中再定义一个委托在新委托中调用方法void Add(int q)。
            Thread t = new Thread(new ThreadStart(delegate {iResult = Add(1000); }));
            t.Name = "子线程";
            //开始新线程
            t.Start();

//设置一个循环来等待子线程结束
            while (t.ThreadState != System.Threading.ThreadState.Stopped)
            {
                t.Join(10);
            }

Debug.WriteLine(iResult.ToString());
            Debug.WriteLine(Thread.CurrentThread.Name);
        }

3,线程池调用 :“线程池”是可以用来在后台执行多个任务的线程集合。这使主线程可以自由地异步执行其他任务。
线程池通常用于服务器应用程序。每个传入请求都将分配给线程池中的一个线程,因此可以异步处理请求,而不会占用主线程,也不会延迟后续请求的处理。
ThreadPool(线程池)是一个静态类,它没有定义任何的构造方法(),我们只能够使用它的静态方法,这是因为,这是因为ThreadPool是托管线程池,是由CLR管理的。
ThreadPool使用WaitCallback委托,它所要做的工作是在后台进行的。使工作项的排队和运行更容易,可以给工作者线程传递一个状态对象(提供数据)。状态对象是私有的作用域位于线程层,所以不需要进行同步。
ThreadPool目标是为了减除线程的初始化开销,实现并行处理。
一个ThreadPool里面注册的线程拥有默认的堆栈大小,默认的优先级。并且,他们都存在于多线程空间(Multithreaded apartment)中。

ThreadPool中的Thread不能手动取消,也不用手动开始。所以ThreadPool并不适用比较长的线程。你要做的只是把一个WaitCallback委托塞给ThreadPool,然后剩下的工作将由系统自动完成。系统会在ThreadPool的线程队列中一一启动线程。

当线程池满时,多余的线程会在队列里排队,当线程池空闲时,系统自动掉入排队的线程,以保持系统利用率。

我们的程序中使用ThreadPool来进行一些比较耗时或者需要阻塞的操作。当学要复杂的同步技术,例如事件,或需要对一个现场表调用Join方法时线程池就不能满足需求了.在以下情况中不宜使用ThreadPool而应该使用单独的Thread

private void button1_Click(object sender, EventArgs e)
        {
           //添加当前主线程名称“main”
            Thread.CurrentThread.Name = "main";
           //使用线程池ThreadPool创建线程
            ThreadPool.QueueUserWorkItem(Add);            
           Debug.WriteLine(Thread.CurrentThread.Name);
        }

//这里加obj参数是为了适应委托格式 public delegate void WaitCallback(object state)
    //包含回调方法要使用的信息的对象。
         void Add(object obj)
        {
            for (int i = 0; i < 100000; i++)
            {
              //输出当前执行操作的现场
                Debug.WriteLine(Thread.CurrentThread.Name+i);
            }
        }

可以同样用委托再委托的方法 调用带有参数(或没有任何参数)的方法

private void button1_Click(object sender, EventArgs e)
        {
            //添加当前主线程名称“main”
            Thread.CurrentThread.Name = "main";
            //使用线程池ThreadPool创建线程
            ThreadPool.QueueUserWorkItem(delegate { Add(333, 44); }, "111");            
            Debug.WriteLine(Thread.CurrentThread.Name);

}

void Add(object obj,int q)
        {
            for (int i = 0; i < q; i++)
            {
                //输出当前执行操作的现场
                Debug.WriteLine(Thread.CurrentThread.Name+i);
            }
        }

分类: C#技术

时间: 2024-10-06 01:11:23

异步调用与线程(总结篇)的相关文章

异步调用主线程UI

this.BeginInvoke(new MethodInvoker(delegate                {                            }));

利用委托实现异步调用

同步调用示例(委托是一个类型安全的,面向对象的指针) using System; using System.Threading; namespace Demo { public delegate int Operate(int x, int y); public class DelegateAsync { static int Add(int a, int b) { Console.WriteLine ("Add() invoked on thread:{0}\n", Thread.C

[转载]C#异步调用四大方法详解

C#异步调用四大方法是什么呢?C#异步调用四大方法的使用是如何进行的呢?让我们首先了解下什么时候用到C#异步调用: .NET Framework 允许您C#异步调用任何方法.定义与您需要调用的方法具有相同签名的委托:公共语言运行库将自动为该委托定义具有适当签名的 BeginInvoke 和 EndInvoke 方法. BeginInvoke 方法用于启动C#异步调用.它与您需要异步执行的方法具有相同的参数,只不过还有两个额外的参数(将在稍后描述).BeginInvoke 立即返回,不等待C#异步

python 37 同步、异步调用

目录 1. 阻塞与非阻塞 2. 同步与异步 2.1 异步调用 2.2 同步调用 2.3 异步调用回收的第一种方式 3. 异步调用+回调函数 3.1 requests模块 3.2 异步调用回收的第二种方式 1. 阻塞与非阻塞 执行的角度: ? 阻塞:阻塞调用是指调用结果返回之前,当前线程会被挂起(如遇到io操作).函数只有在得到结果之后才会将阻塞的线程激活. ? 非阻塞:程序没有遇到IO阻塞,或者程序遇到IO,通过某种方式,让CPU强行运行程序. 2. 同步与异步 发布的角度: ? 同步调用:在发

线程池 异步I/O线程 &lt;第三篇&gt;

在学习异步之前先来说说异步的好处,例如对于不需要CPU参数的输入输出操作,可以将实际的处理步骤分为以下三步: 启动处理: 实际的处理,此时不需要CPU参数: 任务完成后的处理: 以上步骤如果仅仅使用一个线程,当线程正在处理UI操作时就会出现“卡”的现象. 如果使用异步的处理方式,则这三步处理过程涉及到两个线程,主线程中启动第一步:第一步启动后,主线程结束(如果不结束,只会让该线程处于无作为的等待状态):第二步不需要CPU参与:第二步完成之后,在第二个线程上启动第三步:完成之后第二个线程结束.这样

谈.Net委托与线程——创建无阻塞的异步调用

前言 本文大部分内容来自于mikeperetz的Asynchronous Method Invocation及本人的一些个人体会所得,希望对你有所帮助.原英文文献可以在codeproject中搜索到. 介绍 这篇文章将介绍异步调用的实现机制及如何调用异步方法.大多数.NET开发者在经过delegate.Thread.AsynchronousInvocation之后,通常都会对以上概念产生混淆及误用.实际上,以上概念是.NET2.0版本中对并行编程的核心支持,基于概念上的错误认识有可能导致在实际的

谈.Net委托与线程——创建无阻塞的异步调用(一)

前言 本文大部分内容来自于mikeperetz的Asynchronous Method Invocation及本人的一些个人体会所得,希望对你有所帮助.原英文文献可以在codeproject中搜索到. 介绍 这篇文章将介绍异步调用的实现机制及如何调用异步方法.大多数.NET开发者在经过delegate.Thread.AsynchronousInvocation之后,通常都会对以上概念产生混淆及误用.实际上,以上概念是.NET2.0版本中对并行编程的核心支持,基于概念上的错误认识有可能导致在实际的

BeginInvoke 方法真的是新开一个线程进行异步调用吗?

转自原文BeginInvoke 方法真的是新开一个线程进行异步调用吗? 参考以下代码: public delegate void treeinvoke(); private void UpdateTreeView() { MessageBox.Show(System.Threading.Thread.CurrentThread.Name); } private void button1_Click(object sender, System.EventArgs e) { System.Threa

tornado 异步调用系统命令和非阻塞线程池

项目中异步调用 ping 和 nmap 实现对目标 ip 和所在网关的探测 Subprocess.STREAM 不用担心进程返回数据过大造成的死锁, Subprocess.PIPE 会有这个问题. import tornado.gen from tornado.process import Subprocess @tornado.gen.coroutine def run_command(command): """run command""" p