C# 线程与进程

进程与线程


进程

当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源,如Window句柄,文件系统句柄或其他内核对象。每个进程都分配的虚拟内存。

而一个进程又是由多个线程所组成的。

可以打开计算机设备管理查看自己电脑CPU数目,Ctrl+Alt+.调出任务管理器也可以查看,任务管理器还有详细的目前进程数目和线程数目。

进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。

线程

线程是程序中独立的指令流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),一个进程的内存空间是共享的,每个线程都可以使用这些共享空间。即不同的线程可以执行同样的函数。

任何程序在执行时,至少有一个主线程。

多线程



多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。

一个进程的多个线程可以同时运行在不同的CPU上或多核CPU的不同内核上。

多线程的好处:

可以提高CPU的利用率。在多线程程序中,一个线程必须等待的时候,CPU可以运行其它的线程而不是等待,这样就大大提高了程序的效率。

多线程的不利方面:

  1. 线程也是程序,所以线程需要占用内存,线程越多占用内存也越多
  2. 多线程需要协调和管理,所以需要CPU时间跟踪线程
  3. 线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题
  4. 线程太多会导致控制太复杂,最终可能造成很多Bug

操作系统的设计

归结为三点:

  1. 以多进程形式,允许多个任务同时运行
  2. 以多线程形式,允许单个任务分成不同的部分运行
  3. 提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源。

利用异步委托去创建线程



创建线程的一种简单方式是定义一个委托,并异步调用它。

委托是方法的类型安全的引用。Delegate类还支持异步地调用方法。在后台,Delegate类会创建一个执行任务的线程。接下来定义一个方法,使用委托异步调用(开启一个线程去执行这个方法)。

当我们通过BeginInvoke开启一个异步委托的时候,返回的结果是IAsyncResult,我们可以通过它的AsyncWaitHandle属性访问等待句柄。

 1   static string TakesAWhile(int times)
 2   {
 3       Console.WriteLine("异步函数开始!");
 4       Thread.Sleep(times);//程序运行到这里的时候会暂停ms毫秒
 5       return "异步结束!";
 6   }
 7   public delegate string TakesAWhileDelegate(int ms);// 声明委托
 8   static void Main(string[] args)
 9   {
10       TakesAWhileDelegate _delegateTasks = TakesAWhile;
11       IAsyncResult ar = _delegateTasks.BeginInvoke(3000, null, null);
12       while (ar.IsCompleted == false)
13       {
14         Console.Write("-");
15          Thread.Sleep(10);//每隔10mswhile循环一次,Thread类控制线程
16       }
17       Console.WriteLine(_delegateTasks.EndInvoke(ar));
18   }

这个属性返回一个WaitHandler类型的对象,它中的WaitOne()方法可以等待委托线程完成其任务,WaitOne方法可以设置一个超时时间作为参数(要等待的最长时间),如果发生超时就返回false。

 1  static string TakesAWhile(int times)
 2  {
 3    Console.WriteLine("异步函数开始!");
 4    Thread.Sleep(times);//程序运行到这里的时候会暂停ms毫秒
 5    return "异步结束!";
 6  }
 7  public delegate string TakesAWhileDelegate(int ms);// 声明委托
 8  static void Main(string[] args)
 9  {
10
11    TakesAWhileDelegate _delegateTasks = TakesAWhile;
12    IAsyncResult ar = _delegateTasks.BeginInvoke(3000, null, null);
13    while (true)
14    {
15      Console.Write("-");
16      if (ar.AsyncWaitHandle.WaitOne(10, false))// 等待每隔10ms,如果当前异步操作执行完毕时就会返回一个true
17      {
18        Console.WriteLine("异步结束!");
19        break;
20      }
21    }
22    Console.WriteLine(_delegateTasks.EndInvoke(ar));
23  }

等待委托的结果的第3种方式是使用异步回调。在BeginInvoke的第三个参数中,可以传递一个满足AsyncCallback委托的方法,AsyncCallback委托定义了一个IAsyncResult类型的参数其返回类型是void。

对于最后一个参数,可以传递任意对象,以便从回调方法中访问它。(我们可以设置为委托实例,这样就可以在回调方法中获取委托方法的结果)

格式如下:

委托对象.BeginInvoke(委托的参数列表, 回调函数,对象);

例子如下:

 1  static string TakesAWhile(int times)
 2  {
 3    Console.WriteLine("异步函数开始!");
 4             Thread.Sleep(times);//程序运行到这里的时候会暂停ms毫秒
 5             return "异步结束!";
 6         }
 7         public delegate string TakesAWhileDelegate(int ms);// 声明委托
 8  static void Main(string[] args)
 9  {
10
11    TakesAWhileDelegate _delegateTasks = TakesAWhile;
12
13    _delegateTasks.BeginInvoke(3000, TakesAWhileCompleted,_delegateTasks);
14    while (true)
15    {
16      Console.Write("-");
17      Thread.Sleep(10);
18    }
19
20  }
21  static void TakesAWhileCompleted(IAsyncResult ar)
22  {//回调方法是从委托线程中调用的,并不是从主线程调用的,可以认为是委托线程最后要执行的程序
23    if (ar == null) throw new ArgumentNullException("ar");
24    TakesAWhileDelegate _temp = ar.AsyncState as TakesAWhileDelegate;
25    Console.Write(_temp.EndInvoke(ar));
26  }

可以使用在BeginInvoke中使用Lambda表达式,更方便:

1     _delegateTasks.BeginInvoke(3000, ar => Console.WriteLine(_delegateTasks.EndInvoke(ar)), null);
2     while (true)
3     {
4         Console.Write("-");
5         Thread.Sleep(10);
6     }

开启BeginInvoke后,判断线程是否结束的方法,总结如下

  • 利用IAsyncResult中的IsCompleted属性判断是否完成
  • 获取IAsyncResult中的AsyncWaitHandle.WaitOne()线程超时是否,超时的返回参数true
  • 利用BeginInvoke的第三个参数AsyncCallback委托的方法

利用Thread类去创建和控制线程



MSDN查阅地址:

https://msdn.microsoft.com/zh-cn/library/system.threading.thread(v=VS.95).aspx

使用Thread类可以创建和控制线程,并获取其状态。Thread构造函数的参数是一个的委托类型。

例如:

 1     static void Main(string[] args)
 2     {
 3         Thread _nextThread = new Thread(ThreadNext);
 4         _nextThread.Start();//线程开启
 5         Console.WriteLine("主线程运行!");
 6         Thread.Sleep(50);
 7         _nextThread.Abort();//终止线程
 8         Console.WriteLine("线程终止!");
 9     }
10     static void ThreadNext() {
11         while (true)
12         {
13             Console.WriteLine("线程开启—");
14         }
15     }

若要向Thread类传值,有两种方法,第一种:使用带ParameterizedThreadStart委托参数的Thread构造函数

官方描述的Thread类两个构造函数

需注意:线程不会在创建时开始执行。 若要为执行而调度线程,请调用 Start 方法。 若要将数据对象传递给线程,请使用 Start(Object) 方法重载。

特别注意:传递的值为Object对象!

使用例子:

 1         static void Main(string[] args)
 2         {
 3             string str ="线程进行--";
 4             Thread _nextThread = new Thread(ThreadNext);
 5             _nextThread.Start(str);//传入一个对象
 6             Console.WriteLine("主线程运行!");
 7             Thread.Sleep(20);
 8             _nextThread.Abort();//终止线程
 9             Console.WriteLine("线程终止!");
10         }
11         static void ThreadNext(object str) {//这里参数类型必须为object对象!
12             while (true)
13             {
14                 Console.WriteLine(str);
15             }
16         }

还有一种方法:初始化一个对象,对象内部的方法去调用对象里面的成员,线程方法(实例方法)就可以调用内部成员达到传值的目的。

使用例子如下:

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             UserThread myThread = new UserThread("线程开启");
 6             Thread _nextThread = new Thread(myThread.WriteMessage);
 7             _nextThread.Start();//传入一个对象
 8             Console.WriteLine("主线程运行!");
 9             Thread.Sleep(20);
10             _nextThread.Abort();//终止线程
11             Console.WriteLine("线程终止!");
12         }
13     }
14     class UserThread
15     {
16         private string message;
17         public UserThread(string message)
18         {
19             this.message = message;
20         }
21         public void WriteMessage()
22         {
23             while (true)
24             {
25                 Console.WriteLine(message);
26             }
27
28         }
29     }

线程的控制



在默认情况下,用Thread类创建的线程是前台线程。线程池中的线程总是后台线程。

常用的属性和方法:

  1. 线程前后台的控制。在用Thread类创建线程的时候,可以设置IsBackground属性,表示它是一个前台线程还是一个后台线程。
  2. 线程的优先级。Thead类中设置Priority属性,以影响线程的基本优先级 ,Priority属性是一个ThreadPriority枚举定义的一个值。定义的级别有Highest ,AboveNormal,BelowNormal 和 Lowest
  3. 线程插入。可以调用Thread对象的Join方法,表示把Thread加入进来,停止当前线程,并把它设置为WaitSleepJoin状态,直到加入的线程完成为止
  4. 终止线程。使用Thread对象的Abort()方法可以停止线程。
  5. 开始线程的。将当前实例的状态更改为 ThreadState.Running。
  6. 睡眠当前线程。Thread.Sleep()方法可以让当前线程休眠进入WaitSleepJoin状态

线程争用问题:

当程序较大时,运行程序的时候,在计算机有限的资源下,无法避免会产生多个线程争用的问题,对数据进行多次或没有修改。

解决方案为使用Lock关键字,锁住引用对象。Lock只能锁引用对象!

操作如下:

当数据非应用类型,我们可以通过在对象初始化时,同时初始化一个object类型的变量sync,每次修改数据对象时都先锁定sync对象。

使用例子:

static void RaceCondition(object o ){
    StateObject state = o as StateObject;
    int i = 0;
    while(true){
        lock(state){
            state.ChangeState(i++);
        }

    }
}

或者

private object sync = new object();
public void ChangeState(int loop){
    lock(sync){
        if(state==5){
            state++;
            Console.WriteLine("State==5:"+state==5+"  Loop:"+loop);
        }
        state = 5;
    }
}

线程死锁问题:

同时出现两个锁,两个线程都在等另一个线程解锁。

 1 public class SampleThread{
 2     private StateObject s1;
 3     private StateObject s2;
 4     public SampleThread(StateObject s1,StateObject s2){
 5         this.s1= s1;
 6         this.s2 = s2;
 7     }
 8     public void Deadlock1(){
 9         int i =0;
10         while(true){
11             lock(s1){
12                  lock(s2){
13                     s1.ChangeState(i);
14                     s2.ChangeState(i);
15                     i++;
16                     Console.WriteLine("Running i : "+i);
17                 }
18             }
19         }
20     }
21     public void Deadlock2(){
22         int i =0;
23         while(true){
24             lock(s2){
25                  lock(s1){
26                     s1.ChangeState(i);
27                     s2.ChangeState(i);
28                     i++;                                    Console.WriteLine("Running i : "+i);
29                 }
30             }
31         }
32     }
33 }
34 var state1 = new StateObject();
35 var state2 = new StateObject();
36 new Task(new SampleTask(s1,s2).DeadLock1).Start();
37 new Task(new SampleTask(s1,s2).DeadLock2).Start();

死锁现象

解决方法就是一开始就设定好锁定的先后顺序。

线程池



 线程池不需要自己创建,ThreadPool类是系统提供管理线程的线程池类。这个类会在需要时增减池中线程的线程数,直到达到最大的线程数。 池中的最大线程数是可配置的。

创建线程需要时间。 如果有不同的小任务要完成,就可以事先创建许多线程 , 在应完成这些任务时发出请求。 这个线程数最好在需要更多的线程时增加,在需要释放资源时减少。。

在双核 CPU中 ,默认设置为1023个工作线程和 1000个 I/o线程。也可以指定在创建线程池时应立即启动的最小线程数,以及线程池中可用的最大线程数。 如果有更多的作业要处理,线程池中线程的个数也到了极限,最新的作业就要排队,且必须等待线程完成其任务。

任务



在.NET4 新的命名空间System.Threading.Tasks包含了类抽象出了线程功能,在后台使用的ThreadPool进行管理的。

任务表示应完成某个单元的工作。这个工作可以在单独的线程中运行,也可以以同步方式启动一个任务。

启动任务三种方法:

    ///TaskMethod表示一个委托方法
    /// <summary>
    /// 启动任务t1
    /// </summary>
    TaskFactory _Taskfactory = new TaskFactory();
    Task t1 = _Taskfactory .StartNew(TaskMethod);

    /// <summary>
    /// 启动任务t2
    /// </summary>
    Task t2 = TaskFactory.StartNew(TaskMethod);

    /// <summary>
    /// 启动任务t3
    /// </summary>
    Task t3 = new Task(TaskMethod);
    t3.Start();

连续任务

连续任务的特点是,连续任务的开启必要条件是上一个任务已经完成。也就是说:

如果一个任务t1的执行是依赖于另一个任务t2的,那么就需要在这个任务t2执行完毕后才开始执行t1。这个时候我们可以使用连续任务。

 1     static void DoFirst(){
 2     Console.WriteLine("开始任务 : "+Task.CurrentId);
 3     Thread.Sleep(3000);
 4 }
 5     static void DoSecond(Task t){//t为上一个任务
 6     Console.WriteLine("任务"+t.Id+" finished.");//上一个任务已完成
 7     Console.WriteLine("this task id is "+Task.CurrentId);
 8     Thread.Sleep(3000);
 9 }
10     static void Main(string[] args){
11         Task t1 = new Task(DoFirst);
12         Task t2 = t1.ContinueWith(DoSecond);
13 }

任务的层次结构

我们在一个任务中启动一个新的任务,相当于新的任务是当前任务的子任务,两个任务异步执行,如果父任务执行完了但是子任务没有执行完,它的状态会设置为WaitingForChildrenToComplete,只有子任务也执行完了,父任务的状态就变成RunToCompletion。

时间: 2024-10-02 01:56:00

C# 线程与进程的相关文章

python 学习总结1 线程与进程

第一次写博客 进程的定义: 程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程.程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本:进程是程序的一次执行活动,属于动态概念. 在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行.这是这样的设计,大大提高了CPU的利用率.进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的 有了进程为什么还要线程? 进程有很多优

操作系统-线程与进程

首先要分清楚进程和线程的区别. 借用<操作系统概念>的一幅图: 线程由线程ID.程序计数器.寄存器集合和栈组成. 进程由代码段.数据段.文件.线程组成. 可以想象成一个进程可以有多个线程.而每个线程可以共享进程里面的代码段.数据段等. 举个例子. #include <stdio.h> #include <pthread.h> static int counter = 0; void* mythread(void* arg) { int i; for (i = 0; i

iOS开发 - 线程与进程的认识与理解

进程: 进程是指在系统中正在运行的一个应用程序,比如同时打开微信和Xcode,系统会分别启动2个进程; 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内; 线程: 一个进程要想执行任务,必须得有线程(每一个进程至少要有一条线程),是进程中执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位; 一个进程(程序)的所有任务都在线程中执行; 一个程序有且只有一个主线程,程序启动时创建(调用main来启动),主线程的生命周期是和应用程序绑定,程序退出时,主线程也停止;

线程和进程

线程和进程 进程:狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed). 简而言之,就像qq要以一个整体的形式暴露给操作系统管理,里面包含对各种资源的调用,内存的管理,网络接口的调用等,对各种资源管理的集合就可以称为 进程. 进程要操作CPU必须要先创建一个线程,在当代面向线程设计的计算机结构中,进程是线程的容器. 线程:是程序执行流的最小单元,是一串指令的集合 区别: 1,线程共享内存空间和资源

一个初学者的辛酸路程-线程与进程-10

前言: 不知道从什么时候起,面试总爱问线程与进程是神马?今天让你们爽个够. 主要内容:

Python之路【第七篇】:线程、进程和协程

Python之路[第七篇]:线程.进程和协程 Python线程 Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #!/usr/bin/env python # -*- coding:utf-8 -*- import threading import time   def show(arg):     time.sleep(1)     print 'thread'+str(arg)   for i in

Python基础—线程、进程和协程

今天已是学习Python的第十一天,来干一碗鸡汤继续今天的内容,今天的鸡汤是:超越别人对你的期望.本篇博客主要介绍以下几点内容: 线程的基本使用: 线程的锁机制: 生产者消费之模型(队列): 如何自定义线程池: 进程的基本使用: 进程的锁机制: 进程之间如何实现数据共享: 进程池: 协程的基本使用. 一.线程 1.创建线程 上篇博客已经介绍过如何创建多线程的程序,在这里在复习一下如何创建线程过程以及线程的一些方法: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1

【Python之路Day11】网络篇之线程、进程、协程

目录: 基本概念 线程 进程 协程  一. 基本概念 现在的操作系统,如Unix.Linux.Windows.Mac OS X等,都是支持“多任务”的操作系统. 什么叫”多任务“呢?简单理解,就是我们可以一般上网浏览某车之家的网页,看看喜欢的车型信息:一边打开某易云音乐听听好歌:一边打开某软件股市行情图,不安的盯着曲线图...卧槽,又尼玛跌了!  这就是多任务喽. 多核心的CPU已经很普及了,但是,就是在过去的单核心CPU上,也可以执行多任务. PS: CPU是分时间片的,假设任务1执行0.01

Python:简述 线程、进程和协程

Python线程 定义:Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元. #!/usr/bin/env python # -*- coding:utf-8 -*- import threading import time def show(arg): time.sleep(1) print 'thread'+str(arg) for i in range(10): t = threading.Thread(target=show, args=(i,)) t.start(

Linux 线程与进程,以及通信

http://blog.chinaunix.net/uid-25324849-id-3110075.html 部分转自:http://blog.chinaunix.net/uid-20620288-id-3025213.html 1.首先要明确进程和线程的含义: 进程(Process)是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源分配和调度的一个独立单位.与程序相比,程序只是一组指令的有序集合,它本身没有任何运行的含义,只是一个静态实体.进程是程序在某个数据集上的执行,