[.net 面向对象程序设计进阶] (18) 多线程(Multithreading)(三) 利用多线程提高程序性能(下)

[.net 面向对象程序设计进阶] (18) 多线程(Multithreading)(二) 利用多线程提高程序性能(下)

本节导读:

上节说了线程同步中使用线程锁和线程通知的方式来处理资源共享问题,这些是多线程的基本原理。

.NET 4.0以后对多线程的实现变得更简单了。

本节主要讨论.NET4.0多线程的新特性——使用Task类创建多线程。

读前必备:

A. LINQ使用  [.net 面向对象编程基础] (20) LINQ使用

B. 泛型          [.net 面向对象编程基础] (18) 泛型

1. 线程池ThreadPool 

在介绍4.0以后的多线程新特征之前,先简单说一下线程池。

通过前面对多线程的学习,我们发现多线程的创建和使用并不难,难的在于多线程的管理,特别是线程数量级很多的情况下,如何进行管理和资源释放。需要使用线程池来解决。

简单来说线程池就是.NET提供的存放线程的一个对象容器。

线程池线程分为两类:工作线程和IO线程.
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。

对于线程池,可使用要运行的过程的委托调用 System.Threading.ThreadPool.QueueUserWorkItem(System.Threading.WaitCallback) 方法

下面是一个线程池的示例:

先设置一个创建线程总数静态字段:

 static readonly int totalThreads = 20;

使用线程池创建线程:

//设置最小线程和最大线程数
ThreadPool.SetMinThreads(2, 2);
ThreadPool.SetMaxThreads(20, 20);

for (int i = 0; i < totalThreads; i++)
{
    ThreadPool.QueueUserWorkItem(o =>
    {
        Thread.Sleep(1000);
        int a, b;
        ThreadPool.GetAvailableThreads(out a, out b);
        Console.WriteLine(string.Format("({0}/{1}) #{2} : {3}", a, b, Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));
    });
}
Console.WriteLine("主线程完成");

运行结果如下:

2. Task类

用ThreadPool的QueueUserWorkItem()方法发起一次异步的线程执行很简单,但是该方法最大的问题是没有一个内建的机制让你知道操作什么时候完成,有没有一个内建的机制在操作完成后获得一个返回值。为此,在.NET 4.0 以后,我们可以使用System.Threading.Tasks中的Task类。这也是.NET 4.0以后多线程的推荐做法。

构造一个Task<T>对象,并为泛型T参数传递一个操作的返回类型。

Task类可以使用多种方法创建多线程,下面详细介绍。

2.1 使用Factory属性

Task 实例可以用各种不同的方式创建。 最常见的方法是使用任务的 Factory 属性检索可用来创建用于多个用途的TaskFactory 实例。

例如,要创建运行操作的 Task,可以使用工厂的 StartNew 方法:

//最简单的线程示例
Task.Factory.StartNew(() =>
{
    Console.WriteLine("我是使用Factory属性创建的线程");
});

运行结果如下:

如果想简单的创建一个Task,那么使用Factory.NewStart()来创建,很简便。

如果像对所创建的Task附加更多的定制和设置特定的属性,请继续往下看。

2.2 使用Task实例实现多线程

//简单的Task实例创建线程
Action<object> action = (object obj) =>
{
    Console.WriteLine("Task={0}, obj={1}, Thread={2}", Task.CurrentId, obj.ToString(), Thread.CurrentThread.ManagedThreadId);
};
Task t1 = new Task(action, "参数");
t1.Start();

运行结果如下:

//简写上面实例,并创建100个线程
System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
int m = 100;
Task[] tasks = new Task[m];
for (int i = 0; i < m; i++)
{
    tasks[i] = new Task((object obj) =>
        {
            Thread.Sleep(200);
            Console.WriteLine("Task={0}, obj={1}, Thread={2},当前时间:{3}",
            Task.CurrentId, obj.ToString(),
            Thread.CurrentThread.ManagedThreadId,
            System.DateTime.Now.ToString());
        }, "参数" + i.ToString()
    );
    tasks[i].Start();
}

Task.WaitAll(tasks);
Console.WriteLine("线程耗时:{0},当前时间:{1}" ,watch.ElapsedMilliseconds,System.DateTime.Now.ToString());

运行结果如下:

 2.3 Task传入参数

上面介绍了使用一个Action委托来完成任程,那么给线程中传入参数,就可以使用System.Action<object>来完成。

传入一个参数的示例:

/// <summary>
/// 一个参数的方法
/// </summary>
/// <param name="parameter"></param>
static void MyMethod(string parameter)
{
    Console.WriteLine("{0}", parameter);
}

调用如下:

//Task传入一个参数
Task myTask = new Task((parameter) => MyMethod(parameter.ToString()), "aaa");
myTask.Start();

运行结果如下:

传入多个参数如下:

/// <summary>
/// 多个参数的方法
/// </summary>
/// <param name="parameter1"></param>
/// <param name="parameter2"></param>
/// <param name="parameter3"></param>
static void MyMethod(string parameter1,int parameter2,DateTime parameter3)
{
    Console.WriteLine("{0} {1} {2}", parameter1,parameter2.ToString(),parameter3.ToString());
}

调用如下:

//Task传入多个参数
for (int i = 1; i <= 20; i++)
{
    new Task(() => { MyMethod("我的线程", i, DateTime.Now); }).Start();
    Thread.Sleep(200);
}

运行结果如下:

对于传入多个参数,可以使用无参数委托包装一个多参数的方法来完成。

2.4 Task的结果

要获取Task的结果,在创建Task的时候,就要采用Task<T>来实例化一个Task。

其中的T就是Task执行完成之后返回结果的类型。

通过Task实例的Result属性就可以获取结果。

System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
Task<int> myTask = new Task<int>(() =>
{
    int sum = 0;
    for (int i = 0; i < 10000; i++)
        sum += i;
    return sum;
});
myTask.Start();
Console.WriteLine("结果: {0} 耗时:{1}", myTask.Result,watch.ElapsedMilliseconds);

运行结果如下:

使用Factory属性来完成上面的示例:

//使用Factory属性创建
System.Diagnostics.Stopwatch watchSecond = System.Diagnostics.Stopwatch.StartNew();
Task<int> myTaskSecond = Task.Factory.StartNew<int>(() =>
{
    int sum = 0;
    for (int i = 0; i < 10000; i++)
        sum += i;
    return sum;
});
Console.WriteLine("结果: {0} 耗时:{1}", myTaskSecond.Result, watchSecond.ElapsedMilliseconds);

运行结果如下:

多线程除以上的一些基础知识,在处理各种并行任务和多核编程中的使用,小伙伴可以参考专门关于多线程的书籍学习。

想要完全深入的学习多线程需要慢慢修炼,不断积累。

3. 本节要点:

A.本点简单介绍了线程池ThreadPool的使用;

B.介绍一使用Task进行多线程创建及Tast的参数传入和返回结果。

==============================================================================================

返回目录

<如果对你有帮助,记得点一下推荐哦,如有有不明白或错误之处,请多交流>

<对本系列文章阅读有困难的朋友,请先看《.net 面向对象编程基础》>

<转载声明:技术需要共享精神,欢迎转载本博客中的文章,但请注明版权及URL>

.NET 技术交流群:467189533

==============================================================================================

时间: 2024-10-12 04:15:11

[.net 面向对象程序设计进阶] (18) 多线程(Multithreading)(三) 利用多线程提高程序性能(下)的相关文章

[.net 面向对象程序设计进阶] (15) 缓存(Cache)(二) 利用缓存提升程序性能

[.net 面向对象程序设计进阶] (15) 缓存(Cache)(二) 利用缓存提升程序性能 本节导读: 上节说了缓存是以空间来换取时间的技术,介绍了客户端缓存和两种常用服务器缓布,本节主要介绍一种.NET中特别重要的缓布技术Cache.利用Cache提升程序性能. 1. 缓存Cache的命名空间 .NET中对缓存有两个命名空间 命名空间1:System.Web.Caching 命名空间2:System.Runtime.Caching 引用范围:这两个命名空间,都可以在Web和非WEB应用程序中

[.net 面向对象程序设计进阶] (11) 序列化(Serialization)(三) 通过接口IXmlSerializable实现XML序列化及XML通用类

[.net 面向对象程序设计进阶] (11) 序列化(Serialization)(三) 通过接口IXmlSerializable实现XML序列化及XML通用类 本节导读:本节主要介绍通过序列化接口IXmlSerializable实现XML序列化和反序列化,整理了XML基础操作及序列化的通用类(包括XML及节点的基础读写操作,XML到DataSet\DataTable互转换操作,XML序列化及反序列化通用方法等). 读前必备: A.类和类的实例 [.net 面向对象编程基础]  (9) 类和类的

[.net 面向对象程序设计进阶] (7) Lamda表达式(三) 表达式树高级应用

[.net 面向对象程序设计进阶] (7) Lamda表达式(三) 表达式树高级应用 本节导读:讨论了表达式树的定义和解析之后,我们知道了表达式树就是并非可执行代码,而是将表达式对象化后的数据结构.是时候来引用他解决问题.而本节主要目的就是使用表达式树解决实际问题. 读前必备: 本节学习前,需要掌握以下知识: A.继承 [.net 面向对象编程基础]  (12) 面向对象三大特性——继承 B.多态 [.net 面向对象编程基础]  (13) 面向对象三大特性——多态 C.抽象类 [.net 面向

[.net 面向对象程序设计进阶] (1) 开篇

[.net 面向对象程序设计进阶] (1) 开篇 上一系列文章<.net 面向对象编程基础>写完后,很多小伙伴们希望我有时间再写一点进阶的文章,于是有了这个系列文章.这一系列的文章中, 对于.net 基础的一些知识,推荐小伙伴们阅读一下我上一系列文章<.net 面向对象编程基础> ,也就是说本篇文章在阅读前,最好是掌握了.net 的基础知识. 首先,“.net 面向对象程序设计进阶”这一系列的文章涉及的范围比较广,每一节相当于.net的一个分支,基本可以作为一个独立的课题了.只所以

[.net 面向对象程序设计进阶] (17) 多线程(Multithreading)(二) 多线程高级应用

[.net 面向对象程序设计进阶] (17) 多线程(Multithreading)(二) 多线程高级应用 本节要点: 上节介绍了多线程的基本使用方法和基本应用示例,本节深入介绍.NET多线程中的高级应用. 主要有在线程资源共享中的线程安全和线程冲突的解决方案:多线程同步,使用线程锁和线程通知实现线程同步. 1. ThreadStatic特性 特性:[ThreadStatic] 功能:指定静态字段在不同线程中拥有不同的值 在此之前,我们先看一个多线程的示例: 我们定义一个静态字段: static

[.net&#160;面向对象程序设计进阶]&#160;(16)&#160;多线程(Multithreading)(一) 使用多线程提高程序性能

[.net 面向对象程序设计进阶] (16) 多线程(Multithreading)(一) 使用多线程提高程序性能 本节导读: 多线程(Multithreading)使我们程序可以同时进行多任务处理,直接提高了程序的执行效率,学习多线程对提高程序运行能力非常必要,本节主要介绍多线程原理及.NET中多线程的应用. 1. 关于多线程 在介绍多线程之前,先了解一下进程. 进程:独立运行的程序称为进程.(比如Windows系统后台程序,也可以称为后台进程) 线程:对于同一个程序分为多个执行流,称为线程.

[.net 面向对象程序设计进阶] (4) 正则表达式 (三) 表达式助手

[.net 面向对象程序设计进阶] (2) 正则表达式(三) 表达式助手 上面两节对正则表达式的使用及.NET下使用正则表达式作了详细说明,本节主要搜集整理了常用的正则表达式提供参考. 此外为了使用方便,编写了一个<正则表达式助手>方便测试正则表达式. 1.正则表达式助手 1.1 软件概述 软件名称: <正则表达式助手> 版本: 1.0 最后更新日期: 2015/07/01 作者: YuBinfeng 运行环境: .NET Framework 4.6及以上(常用表达式功能,需要联网

[.net 面向对象程序设计进阶] (13) 序列化(Serialization)(五) JSON序列化利器 Newtonsoft.Json 及 通用Json类

[.net 面向对象程序设计进阶] (13) 序列化(Serialization)(五) JSON序列化利器 Newtonsoft.Json 及 通用Json类 本节导读: 关于JSON序列化,不能不了解Json.net(Newtonsoft.Json)这款世界级的开源类库,除了拥有良好的性能之外,功能也是非常强大的. 本节会详细说明这个类库.此外,对于不喜欢使用第三方类库的同学,会整理一个基于微软类库的通用Json类. 读前必备: 本节主要介绍一款第三方类库和一个自己整理封装的类库,说起到封装

[.net 面向对象程序设计进阶] (2) 正则表达式(一)

[.net 面向对象程序设计进阶] (2) 正则表达式(一) 1.什么是正则表达式? 1.1正则表达式概念 正则表达式,又称正则表示法,英文名:Regular Expression(简写为regex.regexp或RE),是计算机科学的一个重要概念.他是用一种数学算法来解决计算机程序中的文本检索.区配等问题. 1.2正则表达式语言支持  正则表达式其实与语言无关,在很多语言中都提供了支持 ,包括最常用的脚本语言Javascript.当然C#语言也毫不例外的提供了很好的支持.     正则表达式语