多线程实践——概述与概念

   C#支持通过多线程并行地执行代码,一个线程有它独立的执行路径,能够与其它的线程同时地运行。一个C#程序开始于一个单线程,这个单线程是被CLR和操作系统(也称为“主线程”)自动创建的,并具有多线程创建额外的线程。  

  一个简单示例如下:

using System;
using System.Threading;

class ThreadDemo
 {
     static void Main()
     {
        Thread t = new Thread (WriteY);
        t.Start();
        while (true) Console.Write ("x");
      }

      static void WriteY()
     {
        while (true) Console.Write ("y");
      }
}

  主线程创建了一个新线程“t”,它运行了一个重复打印字母"y"的方法,同时主线程重复打印字母“x”。CLR分配每个线程到它自己的内存堆栈上,来保证局部变量的分离运行。在接下来的方法中我们定义了一个局部变量,然后在主线程和新创建的线程上同时地调用这个方法。

static void Main() {
  new Thread (Go).Start();
  Go();
}

static void Go() {
  for (int cycles = 0; cycles < 5; cycles++)     Console.Write (‘?‘);
}

  变量cycles的副本分别在各自的内存堆栈中创建,输出也一样,可预见,会有10个问号输出。当线程们引用了一些公用的目标实例的时候,他们会共享数据。示例如下:

class ThreadDemo {
 bool done;

 static void Main() {
   ThreadTest tt = new ThreadTest();
   new Thread (tt.Go).Start();
   tt.Go();
 }

 void Go() {
   if (!done) { done = true; Console.WriteLine ("Done"); }
 }
}

  在上面的示例中,两个线程都调用了Go方法,它们共享了done字段,这个结果输出的是一个"Done",而不是两个。

  静态字段则提供了另一种在线程间共享数据的方式,下面是一个以done为静态字段的例子:

class ThreadDemo {
 static bool done;    // 静态字段在所有线程中共享

 static void Main()  {
   new Thread (Go).Start();
   Go();
 }

 static void Go()  {
   if (!done) { done = true; Console.WriteLine ("Done"); }
 }
}

  上述两个示例足以说明另一个关键概念, 即线程安全(或反之,它的不足之处! ) 输出实际上是不确定的:它可能(虽然不大可能) , "Done" ,可以被打印两次。然而,如果我们在Go方法里调换指令的顺序, "Done"被打印两次的机会会大幅地上升,如:

static void Go() {
  if (!done) { Console.WriteLine ("Done"); done = true; }
}

  问题就是一个线程在判断if块的时候,正好另一个线程正在执行WriteLine语句——在它将done设置为true之前。补救措施是当读写公共字段的时候,提供一个排他锁;C#提供了lock语句来达到这个目的:

class ThreadSafe {
  static bool done;
  static object locker = new object();

  static void Main()   {
    new Thread (Go).Start();
    Go();
  }

  static void Go()   {
    lock (locker)    {
      if (!done) { Console.WriteLine ("Done"); done = true; }
    }
  }
}

  当两个线程争夺一个锁的时候(在这个例子里是locker),一个线程等待,或者说被阻止到那个锁变的可用。在这种情况下,就确保了在同一时刻只有一个线程能进入临界区,所以"Done"只被打印了1次。代码以如此方式在不确定的多线程环境中被叫做线程安全

临时暂停,或阻止是多线程的协同工作,同步活动的本质特征。等待一个排它锁被释放是一个线程被阻止的原因,另一个原因是线程想要暂停或Sleep一段时间:

Thread.Sleep (TimeSpan.FromSeconds (30));   

  一个线程也可以使用它的Join方法来等待另一个线程结束:

Thread t = new Thread (Go);
t.Start();
t.Join();   

  一个线程,一旦被阻止,它就不再消耗CPU的资源了。

线程是如何工作的

线程被一个线程协调程序管理着——一个CLR委托给操作系统的函数。线程协调程序确保将所有活动的线程被分配适当的执行时间;并且那些等待或阻止的线程——比如说在排它锁中、或在用户输入——都是不消耗CPU时间的。

在单核处理器的电脑中,线程协调程序完成一个时间片之后迅速地在活动的线程之间进行切换执行。这就导致“波涛汹涌”的行为,例如在第一个例子,每次重复的X 或 Y 块相当于分给线程的时间片。在Windows XP中时间片通常在10毫秒内选择要比CPU开销在处理线程切换的时候的消耗大的多。(即通常在几微秒区间)

在多核的电脑中,多线程被实现成混合时间片和真实的并发——不同的线程在不同的CPU上运行。这几乎可以肯定仍然会出现一些时间切片, 由于操作系统的需要服务自己的线程,以及一些其他的应用程序。

线程由于外部因素(比如时间片)被中断被称为被抢占,在大多数情况下,一个线程方面在被抢占的那一时那一刻就失去了对它的控制权。

线程 vs. 进程

     属于一个单一的应用程序的所有的线程逻辑上被包含在一个进程中,进程指一个应用程序所运行的操作系统单元。

线程于进程有某些相似的地方:比如说进程通常以时间片方式与其它在电脑中运行的进程的方式与一个C#程序线程运行的方式大致相同。二者的关键区别在于进程彼此是完全隔绝的。线程与运行在相同程序其它线程共享(堆heap)内存,这就是线程为何如此有用:一个线程可以在后台读取数据,而另一个线程可以在前台展现已读取的数据。

何时使用多线程

多线程程序一般被用来在后台执行耗时的任务。主线程保持运行,并且工作线程做它的后台工作。对于Windows Forms程序来说,如果主线程试图执行冗长的操作,键盘和鼠标的操作会变的迟钝,程序也会失去响应。由于这个原因,应该在工作线程中运行一个耗时任务时添加一个工作线程,即使在主线程上有一个有好的提示“处理中...”,以防止工作无法继续。这就避免了程序出现由操作系统提示的“没有相应”,来诱使用户强制结束程序的进程而导致错误。模式对话框还允许实现“取消”功能,允许继续接收事件,而实际的任务已被工作线程完成。BackgroundWorker恰好可以辅助完成这一功能。

在没有用户界面的程序里,比如说Windows Service, 多线程在当一个任务有潜在的耗时,因为它在等待另台电脑的响应(比如一个应用服务器,数据库服务器,或者一个客户端)的实现特别有意义。用工作线程完成任务意味着主线程可以立即做其它的事情。

另一个多线程的用途是在方法中完成一个复杂的计算工作。这个方法会在多核的电脑上运行的更快,如果工作量被多个线程分开的话(使用Environment.ProcessorCount属性来侦测处理芯片的数量)。

一个C#程序称为多线程的可以通过2种方式:明确地创建和运行多线程,或者使用.NET framework的暗中使用了多线程的特性——比如BackgroundWorker类, 线程池threading timer,远程服务器,或Web Services或ASP.NET程序。在后面的情况,人们别无选择,必须使用多线程;一个单线程的ASP.NET web server不是太酷,即使有这样的事情;幸运的是,应用服务器中多线程是相当普遍的;唯一值得关心的是提供适当锁机制的静态变量问题。

何时不要使用多线程

多线程也同样会带来缺点,最大的问题是它使程序变的过于复杂,拥有多线程本身并不复杂,复杂是的线程的交互作用,这带来了无论是否交互是否是有意的,都会带来较长的开发周期,以及带来间歇性和非重复性的bugs。因此,要么多线程的交互设计简单一些,要么就根本不使用多线程。除非你有强烈的重写和调试欲望。

  当用户频繁地分配和切换线程时,多线程会带来增加资源和CPU的开销。在某些情况下,太多的I/O操作是非常棘手的,当只有一个或两个工作线程要比有众多的线程在相同时间执行任务块的多。稍后我们将实现生产者/耗费者 队列,它提供了上述功能。

时间: 2024-10-25 22:16:19

多线程实践——概述与概念的相关文章

FA_固定资产六大业务增加、修改、报废、在建、折旧、盘点概述(概念)

2014-06-04 BaoXinjian 一.汇总 固定资产模组的6大基本业务的流程概述 资产模块的标准流程资产模块五大业务流程,增加.调整.转移.折旧.报废 ?资产的增加外部购入.自行建造.盘盈等 ?资产的管理资产的调整.折旧.报废等 ?资产模块报表和查询通过提交请求生成报表.对资产财务信息的查询 1. 固定资产增加 2. 固定资产修改 3. 固定资产报废 4. 固定资产在建工程 5. 固定资产折旧 6. 固定资产盘点 二.分步解析 1. 固定资产增加 2. 固定资产修改 3. 固定资产报废

OAF_OAF架构MVC系列 - Control的概述(概念)

2014-06-18 BaoXinjian 一.摘要 Control层位于Model层和View层的中间,连接了Model层和View层, 主要存在两个功能 操作/初始化UI和数据 接受和处理页面上的用户的各种事件,并进行分发 本文的基本结构 Designand Create an OA Controller - 基本概念和建立 Handling an HTTP GET - 如何处理HTTP GET请求 Data层面 - 进行初始化 WebBean层面 WebBean层面 - 动态修改WebBe

OAF_OAF架构MVC系列 - View的概述(概念)

2014-06-18 BaoXinjian 一.摘要 上文介绍在OAF按MVC的架构,分为三层,即Model-View-Contorl, 对OAF 的View层的一下基本概念和一些小案例 Page - 页面概述 Page的关键属性 Item的关键属性 Reusable Components - 可重用组件 控件的继承Extend 控件的共享Shared Attribute Sets - 控件属性组 通过Item的Attribute Set 属性手工设定 通过attrSet.setAttribut

[Android学习笔记]Android中多线程开发的一些概念

线程安全: 在多线程的情况下,不会因为线程之间的操作而导致数据错误. 线程同步: 同一个资源,可能在同一时间被多个线程操作,这样会导致数据错误.这是一个现象,也是一个问题,而研究如何解决此类问题的相关工作就叫做线程同步. android中,处理线程同步的手段就是:锁 一般分为公平锁和非公平锁: synchronized(内部锁,互斥锁):synchronized是JVM提供的线程同步机制,如果出现问题,JVM能捕获异常,并释放资源,具体实现机制需要查看JVM源码 synchronized的使用特

Others_Oracle Erp 11i 和 R12的区别概述(概念)

20140-06-26 BaoXinjian 一.不同点1 1.架构变了,原来的SOB现在叫Ledger 2.客户.供应商.税提升了一个级别 3.客户.供应商.银行改为web界面的 4.AR.AP可以支持在一个职责下跨OU查询和录入. 5.引入SLA架构,数据逻辑更清晰.另外从子模块到总账的数据形式和生成的摘要都可编辑.简化了开发的工作量. 二.不同点2 1. .在原来的应收/应付/项目/CST和GL之间增加了一个层次SLA(SUBLEDGER ACCOUNT 子分类帐),通过这个层次来进一步支

多线程实践—Python多线程编程

多线程实践 前面的一些文章和脚本都是只能做学习多线程的原理使用,实际上什么有用的事情也没有做.接下来进行多线程的实践,看一看在实际项目中是怎么使用多线程的. 图书排名示例 Bookrank.py: 该脚本通过单线程进行下载图书排名信息的调用 ? 1 from atexit import register 2 from re import compile 3 from threading import Thread 4 from time import sleep, ctime 5 import

一份针对于新手的多线程实践--进阶篇

前言 在上文<一份针对于新手的多线程实践>留下了一个问题: 这只是多线程其中的一个用法,相信看到这里的朋友应该多它的理解更进一步了. 再给大家留个阅后练习,场景也是类似的: 在 Redis 或者其他存储介质中存放有上千万的手机号码数据,每个号码都是唯一的,需要在最快的时间内把这些号码全部都遍历一遍. 有想法感兴趣的朋友欢迎在文末留言参与讨论????. 网友们的方案 我在公众号以及其他一些平台收到了大家的回复,果然是众人拾柴火焰高啊. 感谢每一位参与的朋友. 其实看了大家的方案大多都想到了数据肯

Linux多线程实践(一)线程基本概念和理论

线程概念 在一个程序里的一个执行路线就叫做线程(thread).更准确的定义是:线程是"一个进程内部的控制序列/指令序列"; 对于每个进程至少有一个执行线程; 进程  VS. 线程  1.进程是资源分配(进程需要参与资源的竞争)的基本单位,而线程是处理器调度(程序执行)的最小单位; 2.线程共享进程数据,但也拥有自己的一部分数据,如 线程ID.一组寄存器.堆栈.errno(错误代码).信号状态.优先级等; 3.一个进程内部的线程可以共享资源,如代码段.数据段.打开文件和信号等(如下图所

iOS开发:多线程技术概述

一.概述 线程(thread):用于指代独立执行的代码段. 进程(process):用于指代一个正在运行的可执行程序,它可以包含多个线程. 任务(task):用于指代抽象的概念,表示需要执行工作. 多线程的替代方法: Operation objects(操作对象):操作对象可能创建线程更快,因为它们使用内核里面常驻线程池里面的线程来节省创建的时间,而不是每次都创建新的线程. Grand Central Dispatch(GCD):如果你更关注你任务的完成而不是线程的管理,那么GCD是很好的选择,