原文地址:C#多线程之旅(1)——介绍和基本概念

C#多线程之旅目录:

C#多线程之旅(1)——介绍和基本概念

C#多线程之旅(2)——创建和开始线程

C#多线程之旅(3)——线程池

C#多线程之旅(4)——APM初探

C#多线程之旅(5)——同步机制介绍

C#多线程之旅(6)——详解多线程中的锁

更多文章正在更新中,敬请期待......

C#多线程之旅(1)——介绍和基本概念

一、多线程介绍

C#通过多线程支持并行执行的代码。一个线程是一个独立执行的路径,可以同时与其他线程一起运行。一个C#客户端程序(Console,WPF,Winows Forms)开始于一个单独的线程,该线程由CLR和操作系统自动地创建,我们称它为主线程,而且可以通过创建附加的线程来实现多线程。

所有的例子都假设引入了以下的namespaces:

Using System;

Using System.Threading;

1.初探

  class Program
{
static void Main(string[] args)
{
Thread thread = new Thread(WriteY);//创建一个线程
thread.Start();//开始一个线程 for (int i = ; i < ; i++)//主线程执行循环
{
Console.Write("x");
} Console.ReadLine();
}
static void WriteY()
{
for (int i = ; i < ; i++)
{
Console.Write("y");
}
} }

一旦开始,一个线程的IsAlive属性返回true,直到这个线程结束。当传递给线程的构造函数的委托完成执行时,这个线程结束。一旦结束,这个线程不能重启。

2.内存隔离

CLR给每个线程分配自己内存栈,因此局部变量可以保持分离。在下一个例子中,我们定义了一个

使用局部变量的方法,然后在主线程和子线程同时调用这个方法。

 class Program
{
static void Main(string[] args)
{
new Thread(Go).Start();
Go();
Console.ReadKey();
} static void Go()
{
for (int i = ; i < ; i++)
{
Console.Write("y");
}
}
}

因为每个线程的内存栈都有一份隔离的循环变量的拷贝,因此可以推断出,输出结果是10个“y”字符 。

3.数据共享

如果多个线程对同一个对象实例有相同的引用,这些线程就共享这个对象实例的数据。例如:

 class Program
{
bool done = false;
static void Main(string[] args)
{
Program p= new Program();
new Thread(p.Go).Start();
p.Go();
Console.ReadKey();
} void Go()
{
if (!done)
{
done = true;
Console.WriteLine("Done");
}
}
}

因为两个线程都调用实例p的go的方法,因此他们共享done这个字段,结果是done只打印出一次而不是两次。

静态字段提供另外一种共享数据的方法:

 class ThreadTest
{
  static bool done;    // Static fields are shared between all threads
 
  static void Main()
  {
    new Thread (Go).Start();
    Go();
  }
 
  static void Go()
  {
    if (!done) { done = true; Console.WriteLine ("Done"); }
  }
}

4.线程安全

这两个例子展示了另外一个重要的概念:线程安全确实是不确定的:done可能被打印出两次(尽管是不太可能发生的)。当我们把Go方法中的语句的顺序交换下,打印出两次done的几率显著提升。

 class Program
{
static bool done = false;
static void Main(string[] args)
{
Program p = new Program();
new Thread(p.Go).Start();
p.Go();
Console.ReadKey();
} void Go()
{
if (!done)
{
Console.WriteLine("Done");
done = true;
}
}
}

这个地方的问题是线程A在线程B设置done等于true之前进入if条件判断中,所有A有机会打印出"Done"。

改进方式当读\写一个公共字段时,获取一个独占锁(exclusive lock)。C#提供了关键字lock。

 class Program
{
static bool done = false;
static readonly object locker = new object();
static void Main(string[] args)
{
new Thread(Go).Start();
Go();
Console.ReadKey();
} static void Go()
{
lock (locker)
{
if (!done)
{
Console.WriteLine("Done");
done = true;
}
}
}
}

当两个线程同时抢占一个锁时(在这个例子中,locker),一个线程等待,或者阻塞,知道这个锁释放。在这个例子中,这个锁保证一次只有一个线程可以进入代码的临界区域,然后“Done”只会被打印一次。代码在这种不确定的多线程背景下中被保护被叫做线程安全。

注意:在多线程中,共享数据是造成复杂原因的主要,而且会产生让人费解的错误。尽管很基本但还是要尽可能保持简单。

一个线程,当阻塞的时候,不占用CPU资源。

二、Join 和Sleep

1.Join

通过调用一个线程的Join方法,可以等待另外一个线程结束。例如:

 static void Main(string[] args)
{
Thread t = new Thread(Go);
t.Start();
t.Join();
Console.WriteLine("Thread t has ended!");
Console.ReadKey(); }
static void Go()
{
for (int i = ; i < ; i++)
{
Console.Write("y");
}
}

这个会打印字符"y"1000次,然后紧接着立刻打印"Thread t has ended!"。Join有多个重载方法,可以在Join方法中添加一个参数,milliseconds或者timeSpan。如果这个线程结束了则Join方法返回true,如果这个线程超时则返回false。

2.Sleep

Thread.Sleep暂停当前线程一段指定的时间:

Thread.Sleep(TimeSpan.FromHours(1));//sleep一个小时

Thread.Sleep(500);//sleep 500 微秒

当使用Sleep或Join暂停线程时,这个线程是阻塞的,不消耗CPU资源。

Thread.Sleep(0)立即放弃这个线程的时间片,主动交出CPU给其他线程。Framework 4.0的新方法Thread.Yield()方法做同样的事,除了当它仅仅在同一个进程中时,才会放弃时间片。

Sleep(0)或Yield()有时候对提升产品性能有用。而且它们也是诊断工具可以帮助揭开线程安全的问题;

如果在代码中的任何地方都插入Thread.Yield(),会造成bug。

三、线程怎样工作

1.多线程由一个线程调度器来进行内部管理,一个功能是CLR常常委托给操做系统。

一个线程调度器确保所有激活的线程在执行期间被合适的分配,等待或者阻塞的线程(比如,一个独占锁或者等待用户输入)不占用CPU资源。

2.在单核电脑上,一个线程调度器让时间片在每一个激活的线程中切换。在windows操作系统下,线程切换的时间分片通常为10微秒,远远大于CPU的开销时间(通常小于1微秒)。

3.在一个多核的电脑上,多线程实现了一个混合的时间片和真正的并发,不同的线程同时在不同的CPU上执行代码。还是存在某些时间片,因为操作系统需要服务它自己的线程,包括其他的应用的线程。

4.当一个线程的执行被内部因素打断,比如时间片,则说这个线程是抢占式的。在大部分情形下,一个线程不能控制自己何时何地被抢占。

四、线程和进程

一个线程类似于你的应用程序正在运行的一个操作系统进程。类似于进程并行运行在一台电脑上,线程并行运行在一个单独的进程中。进程之间是完全隔离的;线程在一定程度上隔离。运行在同一个应用程序下的线程共享堆内存。在某种程度上,这就是为什么线程如此有用:一个线程可以在后台取回数据,比如同时另外一个线程正在显示数据。

五、线程的使用和误用

多线程有许多用途,下面是最通用的:

保持一个可响应的用户界面

通过在一个并行的“worker”线程上运行时间消耗的任务,主UI线程可以空闲地执行键盘或鼠标事件。

使其他阻塞CPU的线程得到最有效的使用

当一个线程正等待另外一计算机或硬件的响应时是非常有用的。当一个线程执行任务时阻塞了,其他线程正好可以使用计算机。

并行编程

如果工作负荷被共享给正在执行“各个击破”策略的多个线程,则代码在多核或多进程中集中计算可以执行得更快。

预测执行

在多核的机器上,你有时通过预测某些事情需要做,然后提前做,从而可以提高性能。LINQPad使用这项技术提高查询的创建。一个变体是运行许多并行的算法去处理同样的任务。无论哪个完成了第一个“wins”-当你预先不知道哪一个算法执行得更快时,这是非常有效的。

允许同时执行请求

在一个server上,客户端请求可以并行抵达,所以需要并行处理。如果你使用ASP.NET,WCF,Web Service或Remoting,.NET Framework 会自动创建线程。这个在client上也是有用的(比如说处理点对点的net working,或者是user的多个请求)。

比如ASP.NET和WCF技术,你可能甚至不会注意到,除非你访问没有合适的locking,违反线程安全的共享数据(假定通过静态字段)。

多线程会带来一系列问题。最大的问题是多线程会提升复杂性。有许多线程本身不会带来复杂性,而是因为线程之间的相互影响(尤其是通过共享数据)。这个适用于是否这个相互影响是故意的,而且这个可以造成长时间的开发周期和一个持续性的敏感性和不可重现的bug。因为这个原因,需要将相互影响降到最低。尽可能坚持和提高可靠的设计。这篇文章主要集中在处理这些复杂性,移除相互影响这个不用多说。

一个好的策略是封装多线程的logic到可复用的类中,这些类可以独立地被测试。这个Framework它自己提供了许多的高级线程构造函数,我们后面再介绍。

线程在调度和切换线程时会造成资源和CPU的消耗(当激活的线程数量多余CPU的核的数量时)-而且有创建/销毁损耗。多线程通常会提升应用程序的速度-但是如果过度或者不适当使用甚至会使应用程序变慢。比如,当硬件I/O被涉及到时,有两个线程串行运行任务比起10个并行线程一次性执行更快。(在等待和脉冲信号中,我们描述怎样实现一个生产者/消费者队列来实现这个功能。)

参考资料:《C# 4.0 in a Nutshell》

C#多线程之旅(1)——介绍和基本概念的更多相关文章

  1. C#多线程之旅(4)——APM初探

    源码地址:https://github.com/Jackson0714/Threads 原文地址:C#多线程之旅(4)——APM初探 C#多线程之旅目录: C#多线程之旅(1)——介绍和基本概念 C# ...

  2. C#多线程之旅(3)——线程池

    v博客前言 先交代下背景,写<C#多线程之旅>这个系列文章主要是因为以下几个原因:1.多线程在C/S和B/S架构中用得是非常多的;2.而且多线程的使用是非常复杂的,如果没有用好,容易造成很 ...

  3. C#多线程之旅(7)——终止线程

    先交代下背景,写<C#多线程之旅>这个系列文章主要是因为以下几个原因:1.多线程在C/S和B/S架构中用得是非常多的;2.而且多线程的使用是非常复杂的,如果没有用好,容易造成很多问题.   ...

  4. iOS开发多线程篇—NSOperation简单介绍

    iOS开发多线程篇—NSOperation简单介绍 一.NSOperation简介 1.简单说明 NSOperation的作⽤:配合使用NSOperation和NSOperationQueue也能实现 ...

  5. 基于MVC4+EasyUI的Web开发框架形成之旅--总体介绍

    最近花了很多时间在重构和进一步提炼Winform开发框架的工作上,加上时不时有一些项目的开发工作,我博客里面介绍Web开发框架的文章比较少,其实以前在单位工作,80%的时间是做Web开发的,很早就形成 ...

  6. 再谈多线程模型之生产者消费者(基础概念)(c++11实现)

    0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现)[本文] 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现) 再谈多线程模型之生 ...

  7. TensorFlow入门,基本介绍,基本概念,计算图,pip安装,helloworld示例,实现简单的神经网络

    TensorFlow入门,基本介绍,基本概念,计算图,pip安装,helloworld示例,实现简单的神经网络

  8. ZooKeeper入门实战教程(一)-介绍与核心概念

    1.ZooKeeper介绍与核心概念1.1 简介ZooKeeper最为主要的使用场景,是作为分布式系统的分布式协同服务.在学习zookeeper之前,先要对分布式系统的概念有所了解,否则你将完全不知道 ...

  9. C#多线程之旅~上车吧?

    前言:前几天,写了一篇关于多线程使用的文章,[线程使用]用法得到不少博友的好评,博主这几天加班写文章,把剩下的高级使用给写完,期望可以得到博友的追赞吧,那么废话不多说,开始我们的C#高级用法之旅!! ...

随机推荐

  1. 51单片机对无线模块nRF24L01简单的控制收发程序

    它的一些物理特性如工作频段.供电电压.数据传输速率就不详细介绍了,直接上代码. 1.首先是发送端: // Define SPI pins #include <reg51.h> #defin ...

  2. centos 6.X minimal 系列最小化安装完成后,安装mono和jexus过程小记录

    在使用虚拟机安装minimal版centos运行mono+jexus的时候,遇到了一些坑,记录一下,比如虚拟机访问不了网络,没wget命令没开放80端口,等等小问题,其他网上教程已经有mono+jex ...

  3. 屏幕适配1(Android 增强版百分比布局库 为了适配而扩展)

    转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/46767825: 本文出自:[张鸿洋的博客] 这篇博文写得太好了,让我很激动,分 ...

  4. php模拟登陆的两种实现方法分析

    php模拟登陆的实现方法分析 本文实例分析了php模拟登陆的实现方法.分享给大家供大家参考.具体分析如下: php模拟登陆的实现方法,这里分别列举两种方法实现模拟登陆人人网.具体实例代码如下: 1)使 ...

  5. WEB开发之路——基础部分

    WEB开发之路 受BBC的<BBC: Brain Story>和<BBC: The Brain - A Secret History>的影响,我一直有志于探究人类大脑,2015 ...

  6. sql 动态语句

    如果动态语句有表变量 例子如下: declare @mS varchar(10) declare @mE varchar(10) declare @mSQL nvarchar(500) --SQL语句 ...

  7. Eclipse - Failed to load the JNI shared Library (JDK)

    When I try opening Eclipse, a pop-up dialog states: Failed to load the JNI shared library "C:/J ...

  8. Webstorm 2016内置web服务器配置

    运行three.js的官方的例子.本来想用IIS来运行,运行不了.所以用webstorm,用鼠标右键的方式,来运行,如下图 但是有一天,我把IIS配置好了,可以在IIS中运行了(只是把build文件夹 ...

  9. 备库Seconds_Behind_Master的计算

    背景 在mysql主备环境下,主备同步过程如下,主库更新产生binlog, 备库io线程拉取主库binlog生成relay log.备库sql线程执行relay log从而保持和主库同步. 理论上主库 ...

  10. 说说WeakReference弱引用

    WeakReference弱引用概述 http://www.cnblogs.com/xrq730/p/4836700.html,关于Java的四种引用状态具体请参看此文 Java里一个对象obj被创建 ...