线程是怎样工作的

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

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

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

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

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

进程与线程

线程并行运行在一个单独的进程中。进程之间是完全隔离的;线程在一定程度上隔离。运行在同一个应用程序下的线程共享堆内存。

进程间的切换:

先加载程序A的上下文,然后开始执行A,保存程序A的上下文,调入下一个要执行的程序B的程序上下文,然后开始执行B,保存程序B的上下文。。。

线程间的切换:

先加载程序A的上下文,然后执行A的A1线程,再执行A的A2线程。。。保存程序A的上下文;

1.线程结束无法重启

class Program
{
static void Main(string[] args)
{
Thread thread = new Thread(threadOne);
thread.Start();
Thread.Sleep(5000);
//thread.Start();打开报错
} static void threadOne()
{
for(int i=0; i<100; i++)
{
lib.put("t"+i);
Thread.Sleep(10);
}
}
}

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

2.内存隔离

class Program
{
static void Main(string[] args)
{
Thread thread = new Thread(threadOne);
thread.Start();
threadOne();//主线程调用
Thread.Sleep(5000);
//thread.Start();打开报错
} static void threadOne()
{
for(int i=0; i<10; i++)
{
lib.put("t");
Thread.Sleep(10);
}
}
}

  CLR给每个线程分配自己内存栈,因此局部变量可以保持分离。所以两次调用threadOne都正常打印了10个"t"。

3. 数据共享

    class Program
{
static bool bDone = false;
static void Main(string[] args)
{
Thread thread = new Thread(threadOne);
thread.Start();
threadOne();
Console.ReadKey();
} static void threadOne()
{
if(!bDone)
{
bDone = true;
lib.put("threadOne");
}
}
}

如果多个线程对同一个对象实例有相同的引用,这些线程就共享这个对象实例的数据。所以只会输出一个threadOne。

4.线程安全

上述例子,是有可能打印出两次threadOne的。当bDone被置成true之前,若主线程和子线程都已经通过if判定,将打印两次。

static void threadOne()
{
if(!bDone)
{
Thread.Sleep();//加上sleep后,会出现主线程和子线程都通过if判定后才把bDone置成true,所以打印两次threadOne,
     bDone = true;
     lib.put("threadOne");
  }
}

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

class Program
{
static bool bDone = false;
static readonly object locker = new object();
static void Main(string[] args)
{
Thread thread = new Thread(threadOne);
thread.Start();
threadOne();
Console.ReadKey();
} static void threadOne()
{
lock (locker)
{
if (!bDone)
{
Thread.Sleep();
bDone = true;
lib.put("threadOne");
}
}
}
}

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

lock关键字将语句块标记为临界区,方法是获取给定对象的互斥锁,执行语句,然后释放该锁。在块的开始处调用 Enter,而在块的结尾处调用 Exit。这样可确保当一个线程位于代码的关键部分时,另一个线程不会进入该关键部分。 如果其他线程尝试进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。一个线程,当阻塞的时候,不占用CPU资源。

5.Join和Sleep

Join是一种同步方法,阻止调用线程 (即,调用的方法的线程),直到线程其Join方法调用已完成。 使用此方法以确保线程已终止。 如果线程不会终止,调用方将无限期阻止。

Join有多个重载方法,可以在Join方法中添加一个参数,milliseconds或者timeSpan。如果这个线程结束了则Join方法返回true,如果这个线程超时则返回false,但不结束该线程。

static void Main(string[] args)
{
Thread thread = new Thread(threadOne);
thread.Start();
thread.Join();//等到thread线程结束后继续本线程
lib.print("end");
Console.ReadKey();
} static void threadOne()
{
lib.print("threadOne");
Thread.Sleep();
}

Sleep暂停当前线程一段指定的时间。

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

Thread.Sleep(500);//500 线程被阻塞的毫秒数。 指定零0以指示应挂起此线程以使其他等待线程能够执行。

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

C# 多线程系列(二)

最新文章

  1. LR12.53—第5课:创建负载测试场景
  2. Silverlight动画之 Animation Easing
  3. node-webkit 应用打包发布
  4. [ActionScript 3.0] AS3.0 获取像素点的灰度
  5. linux下进程相关操作
  6. MarkDown教程
  7. 解决Appium 抓取toast
  8. Java基础笔记4
  9. JS使用小记
  10. python学习之初识字符串
  11. LeetCode - Word Subsets
  12. C# X509Certificate类 调用证书
  13. Mongodb嵌套文档的改动-利用数组改动器更新数据
  14. elasticsearch mapping demo
  15. Mono.Cecil 修改目标.NET的IL代码保存时报异常的处理。
  16. 简单了解 iTextSharp实现HTML to PDF
  17. MyBatis中文文档
  18. SQLite3的运用
  19. 读经典——《CLR via C#》(Jeffrey Richter著) 笔记_引用类型和值类型(二)
  20. Excel打印区域设置

热门文章

  1. chrony配置介绍
  2. Vova and Trophies CodeForces - 1082B(思维题)
  3. ASP.NET大闲话:ashx文件有啥用
  4. (ccf)201703-3markdown
  5. Git 基础教程 之 标签
  6. 微信小程序&#183;前端-锦囊
  7. web开发如何使用高德地图API(三)点击热点打开信息窗体
  8. iOS学习笔记17-FMDB你好!
  9. nyoj_111_分数加减法_201311281341
  10. ArcEngine控制台应用程序