在开发中经常有遇到因为程序执行的时间过长,而造成程序假死的情况,这是因为我们的程序是同步执行的,当执行到需要长时间的操作时,程序就会等待当前的操作完成,从而造成程序假死。C#的异步与多线程就是为了解决这个问题的。

  1. 什么是多线程,举个简单的例子,我们在做饭的时候,可以先煮好饭,然后炒菜,然后洗餐具,然后完成,每一个操作都是在前一个操作完成之后才能进行,这就叫做同步执行,我们也可以在边煮饭的同时炒菜,洗餐具,当所有的工作都做完的时候,饭也就做好了,在这个过程中,煮饭,炒菜是同时进行的,这个就是异步,多线程就类似于主线程在煮饭,然后另开一个新的线程用来炒菜。
  2. 多线程的优缺点,由上所述,多线程可以在新开的线程中执行操作,所以他不需要等到主线程完成,也不收主线程的影响,自然,也就不会造成程序假死的情况出现了。这样可以提高用户的交互体验,防止用户以为程序崩溃也不断重启。但是由于我们直接感受到的是主线程,而新开的线程其实是在后台执行的,所以,如果新开的线程出现了异常,我们很难再主线程中捕获,或者处理,同时新开一个线程也需要计算机硬件的支持,如果线程过多,可能会造成系统变得很卡,资源消费过多的现象。
  3. 什么情况下需要使用多线程的技术?根据多线程的特点,其实类似于一个后台执行的任务,所以一般在以下情况中会使用多线程技术。1、程序执行时间比较长,同时程序的结果不是那么重要,不应该是主线程等待结果的情况下,可以使用多线程异步执行。例如,登录之后的验证过程。2、需要定时刷新的功能,这些功能是定时循环执行,所以可以放在后台去异步执行,这样既能保证功能执行了,同时在执行的过程中也不会造成程序卡顿的现象。3、后台任务,程序只需要执行相关的功能,不需要接收执行结果。例如发送一条短信,我们只需要发送出去即可,不需要知道用户是否接收到了短信,这样的情况下可以使用异步发送。其他情况可以参考以上的情况来决定是否需要使用多线程。

多线程编程示例

.Net提供了多种方式实现多线程的编程,包括线程池,Thread,Task等方法,下面对应这些方法给出简单的示例。

首先,建立一个功能类,此类的作用是多线程需要执行的方法,方法包括,无参数无返回值,有参数无返回值,无参数有返回值,有参数有返回值四种情况。

/// <summary>

/// 此类用于多线程测试的公用方法的定义

/// </summary>

public class CommonClass

{

//注意多线程使用的方法对应的参数类型应该为object

/// <summary>

/// 无返回值有参数的方法,用于无返回值的多线程的类型的使用

/// </summary>

/// <param name="name"></param>

public void ShowName(object name)

{

Console.WriteLine("Your Name Is: {0}", name);

}

/// <summary>

/// 有返回值的方法,用于有返回值的多线程类型的使用

/// </summary>

/// <param name="name"></param>

/// <returns></returns>

public string GetName(object name)

{

return name + "English's name is Lily";

}

}

上面的代码只有两个方法,都带参数,但是一个有返回值,一个没有返回值。对于无参数的方法,只需要把传入的参数设置为null即可。

  1. 使用ThreadPool实现多线程

ThreadPool顾名思义就是线程池,由于创建一个新的线程的代价比较大,所以如果没有必要,就不需要创建一个新的线程。线程池就是为此设计的,当新创建一个线程的时候,首先到线程池中查询是否有空闲线程,如有,则直接使用此线程,这样就避免了新创建一个线程,若无,则新创建一个线程,并加入到线程池中,当线程执行结束之后,当前线程变为空闲线程,并放入线程池,等待下一个调动。当创建的线程数超过线程池允许的最大线程数之后,线程就需要排队,等待空闲线程的出现。

/// <summary>

/// 线程池实现多线程的定义

/// 线程池与Thread的区别在于,Thread每次都是新建一个线程,执行完成后就销毁

/// 而线程池有一个最大线程数,会在池内空闲线程数不够的时候创建新的线程,并存入

/// 线程池,线程执行完成后并不会销毁,而是存入线程池,作为空闲线程,等待下一次调用,

/// 超过线程池最大线程数时,不会再创建新的线程,而是排队等待新的空闲线程,因此,

/// 线程池比Thread的性能更好

/// </summary>

public class ThreadPoolTest

{

//和Thread一样,线程池只能创建无返回值和最多带一个参数的多线程方法

public void CreateThread()

{

//ThreadPool.SetMaxThreads(10, 10);//设置线程池最大线程数的方法

//ThreadPool.SetMinThreads(5, 5);//设置线程池最小线程数的方法

var com = new CommonClass();

for (int i = 2; i < 7; i++)

{

//往线程池中添加5个方法,但是线程池中不一定会创建5个线程

ThreadPool.QueueUserWorkItem(new WaitCallback(com.ShowName), i.ToString());

}

}

}

线程池的方法可以有参数,但是不能有返回值,或者只能返回void。

  1. Thread类实现多线程

Thread用于创建一个线程,他与线程池的区别就在于,他每次都会新创建一个线程,执行完成之后销毁线程。不存在等待空闲线程的概念,性能取决于硬件设备的性能。

/// <summary>

/// 此类用于多线程的Thread类实现测试

/// </summary>

public class ThreadTest

{

//Thread不能创建带有返回值的多线程方法,如果需要请使用Task

/// <summary>

/// 用于创建多线程并行的代码

/// </summary>

public void CreateThread()

{

var com = new CommonClass();

for (int i = 0; i < 5; i++)

{

//ParameterizedThreadStart类型用于定义一个带参数的方法的线程,如果需要定义不带参数的多线程实现,请使用ThreadStart

//同时参数只能有一个,如果需要使用多个参数,请使用其他多线程实现方法,或将多个参数直接封装为一个Class进行传递

Thread t = new Thread(new ParameterizedThreadStart(com.ShowName));

t.Start(i.ToString());

}

}

}

上面代码中红字部分为要执行的方法,从字面意思即可知道,这个方法是需要定义参数的,如果需要定义无参数的方法,则需要使用new ThreadStart(方法名),t.Start()方法用于开始执行线程,方法的参数即为调用的方法需要传入的参数。在上面的示例中,传入的参数即为ShowName方法所需的参数。和ThreadPool一样,Thread也是不能有返回值的。

  1. Task实现多线程

Task即为任务,也是实现多线程的一种方式,他定义的方法必须带一个object的参数,同时可以有返回值。

/// <summary>

/// 通过Task实现多线程的方法

/// Task相比Thread和ThreadPool的区别最直观的就在于可以实现带返回值的方法

/// </summary>

public class TaskTest

{

/// <summary>

/// 使用Task创建不带返回值的多线程

/// 此处的void也可以为Task,在async的异步编程中必须为Task

/// 具体实现请参考CreateReturTaskThread

/// </summary>

public void CreateNoReturnThread()

{

for (int i = 0; i < 5; i++)

{

//此处会形成一个闭包,所以多次返回的结果一样

Task.Run(() =>

{

Console.WriteLine("This Number is {0}", i);

});

//可以通过如下委托的方式解决上面的问题

//Action<int> act = (a) =>

//{

//    Task.Run(() =>

//    {

//        Console.WriteLine("This Number is {0}", a);

//    });

//};

//act(i);

}

}

/// <summary>

/// 返回Task的方法

/// </summary>

/// <returns></returns>

public Task CreateReturTaskThread()

{

return Task.Run(() =>

{

Console.WriteLine("This is a Method for return Task!");

});

}

/// <summary>

/// 创建返回具体值的方法

/// </summary>

/// <returns></returns>

public Task<string> CreateReturnNameThread()

{

return Task.Run(() =>

{

CommonClass com = new CommonClass();

string name = com.GetName("HoS ");

Thread.Sleep(5000);

return name;

});

}

}

在上面的示例中,既有返回void的方法,也有返回具体值的方法,使用Task.Run方法即可定义一个异步执行的方法。Run内部需要传入一个委托,来定义需要异步执行的功能,相比较Thread,代码的是想相对复杂,但是可以有返回值,同时创建一个任务相比建创建一个线程的开销小很多。

  1. async与await关键字

在C#4.0以后为简化异步操作,添加可async与await两个关键字,这两个关键字的内部也是通过Task来实现异步操作,但是如果不添加这两个关键字,那么方法就会以同步执行的方式来执行。同时await会等待方法执行的结果,但是在等待的过程中,不会阻塞主线程,也就不会造成程序假死的现象。

/// <summary>

/// 此类用于测试.Net4.0的async实现异步编程的方法

/// </summary>

public class AsyncTest

{

//1.定义一个返回值为Task<T>的方法

public Task<int> GetSum(List<int> list)

{

return Task.Run(() =>

{

return list.Sum();

});

}

//2.定义一个标识了async的方法

/// <summary>

/// 此方法用async标识,代表这是一个异步执行的方法,此方法不会阻塞当前线程

/// </summary>

public async void ShowSum()

{

List<int> list = new List<int>{ 5, 15, 12, 7, 9, 13, 6, 21 };

//此代码加了await标识,与async配合使用,代表这是一个异步的方法,

//如果不加await方法,则此处代码会同步执行

//需要注意的是await后面的方法需要等到await执行完成之后才会继续执行,

//而不是和后面的代码一起执行,也就是说这里实现了间接的多线程的同步的功能,

//同时不会卡住主线程,可以解决Winform项目中界面的假死的问题

//因为使用了await关键字,所以不需要在使用.Result来获取Task的结果了

int result = await GetSum(list);

Console.WriteLine("Result is {0}",result);

}

}

如上代码所示async是需要添加在方法的定义上面,同时微软建议,所有的async标识的方法返回的都应该是Task<T>如果是返回void则返回Task,上面的方法仅做演示所以未遵照此要求,需要注意。await关键字放在需要异步执行的方法之前即可。

以上就是几种实现多线程的方式。调用的方法也很简单

//注意:多线程由于是异步执行,所以可能造成无法预知的执行顺序,即以下代码每次执行的结果都可能不同

//1.Thread实现

//ThreadTest.Common.ThreadTest tt = new Common.ThreadTest();

//tt.CreateThread();

//2.ThreadPool实现

//ThreadPoolTest tpt = new ThreadPoolTest();

//tpt.CreateThread();

//3.Task实现

//3.1 无返回值的实现

//TaskTest tt = new TaskTest();

//tt.CreateNoReturnThread();

//3.2 返回一个Task的实现

//var result = tt.CreateReturTaskThread();

//3.3 返回一个Task<T>的实现

//var result = tt.CreateReturnNameThread();

//Console.WriteLine(result.Result);

//4.async的实现

//var at = new AsyncTest();

//at.ShowSum();

//5.多线程的同步实现

//5.1 AutoResetEventTest

//LockTest lt = new LockTest();

//lt.AutoResetEventTest();

//5.2 MutexTest

//lt.MutexTest();

//5.3 MulLock

//lt.MulLock();

//5.3  ManualResetEvent实现线程的手动挂起

//lt.MulLockM();

AsyncTest at = new AsyncTest();

at.ShowSumNo();

Console.ReadKey();

上面的代码仅供学习使用,如果需要更加深入的了解各种方式,请参考相关的教程,谢谢!

C#多线程编程笔记的更多相关文章

  1. 数据结构(逻辑结构,物理结构,特点) C#多线程编程的同步也线程安全 C#多线程编程笔记 String 与 StringBuilder (StringBuffer) 数据结构与算法-初体验(极客专栏)

    数据结构(逻辑结构,物理结构,特点) 一.数据的逻辑结构:指反映数据元素之间的逻辑关系的数据结构,其中的逻辑关系是指数据元素之间的前后件关系,而与他们在计算机中的存储位置无关.逻辑结构包括: 集合 数 ...

  2. linux 多线程编程笔记

    一, 线程基础知识 1,线程的概念 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行 中必不可少的资源(如程序计 ...

  3. 【C】多线程编程笔记

    1. pthread_create(pthread类型指针变量 ,NULL ,函数 ,函数参数[多个参数用结构体传]) 2. pthread_join(pthread类型指针变量, 返回一般为null ...

  4. Java基础复习笔记系列 八 多线程编程

    Java基础复习笔记系列之 多线程编程 参考地址: http://blog.csdn.net/xuweilinjijis/article/details/8878649 今天的故事,让我们从上面这个图 ...

  5. 孙鑫VC学习笔记:多线程编程

    孙鑫VC学习笔记:多线程编程 SkySeraph Dec 11st 2010  HQU Email:zgzhaobo@gmail.com    QQ:452728574 Latest Modified ...

  6. 多线程编程学习笔记——async和await(一)

    接上文 多线程编程学习笔记——任务并行库(一) 接上文 多线程编程学习笔记——任务并行库(二) 接上文 多线程编程学习笔记——任务并行库(三) 接上文 多线程编程学习笔记——任务并行库(四) 通过前面 ...

  7. 多线程编程学习笔记——async和await(二)

    接上文 多线程编程学习笔记——async和await(一) 三.   对连续的异步任务使用await操作符 本示例学习如何阅读有多个await方法方法时,程序的实际流程是怎么样的,理解await的异步 ...

  8. 多线程编程学习笔记——async和await(三)

    接上文 多线程编程学习笔记——async和await(一) 接上文 多线程编程学习笔记——async和await(二) 五.   处理异步操作中的异常 本示例学习如何在异步函数中处理异常,学习如何对多 ...

  9. 多线程编程学习笔记——使用异步IO(一)

    接上文 多线程编程学习笔记——使用并发集合(一) 接上文 多线程编程学习笔记——使用并发集合(二) 接上文 多线程编程学习笔记——使用并发集合(三) 假设以下场景,如果在客户端运行程序,最的事情之一是 ...

随机推荐

  1. Java工作原理:JVM,内存回收及其他

    JAVA虚拟机系列文章 http://developer.51cto.com/art/201001/176550.htm Java语言引入了Java虚拟机,具有跨平台运行的功能,能够很好地适应各种We ...

  2. Java for Andriod 第二周学习总结

    第四章 学习时遇到的问题或新知识点: 1. 构造方法.每个类至少有一个构造方法,且构造方法必须的名称必须与类名相同. 2. Varargs.允许方法拥有一个可变长度的参数列表. 3. 对象的内存分配. ...

  3. vue2.0 如何自定义组件(vue组件的封装)

    一.前言 之前的博客聊过 vue2.0和react的技术选型:聊过vue的axios封装和vuex使用.今天简单聊聊 vue 组件的封装. vue 的ui框架现在是很多的,但是鉴于移动设备的复杂性,兼 ...

  4. 把一下程序中的print()函数改写成

    源代码: #include <iostream> using namespace std; void print( int w ) { ; i <= w ; i++ ) { ; j ...

  5. 微信报错 config:fail.Error:invalid signature

    config:fail.Error:invalid signature 微信公众号报这个错,appid等各项都配置好,经过一番折腾,发现原来ip白明单设置了不是该服务器的ID,重新设置后就可以了

  6. 让 ComboBox 的每个栏目显示不同颜色

    在一般的应用程式中,使用 ComboBox 提供下拉选单的功能,让使用者选择不同项目,一般而言, ComboBox 的项目没有什么特别的,但在特定的应用程式中,有时候会希望每个项目有一些效果呈现,例如 ...

  7. 给ASP.NET Core Web发布包做减法

    1.引言 紧接上篇:ASP.NET Core Web App应用第三方Bootstrap模板.这一节我们来讲讲如何优化ASP.NET Core Web发布包繁重的问题. 在ASP.NET Core W ...

  8. [Swift]LeetCode223. 矩形面积 | Rectangle Area

    Find the total area covered by two rectilinear rectangles in a 2D plane. Each rectangle is defined b ...

  9. [Swift]LeetCode575. 分糖果 | Distribute Candies

    Given an integer array with even length, where different numbers in this array represent different k ...

  10. 【阿里面试系列】Java线程的应用及挑战

    文章简介 上一篇文章[「阿里面试系列」搞懂并发编程,轻松应对80%的面试场景]我们了解了进程和线程的发展历史.线程的生命周期.线程的优势和使用场景,这一篇,我们从Java层面更进一步了解线程的使用.关 ...