• 线程



     

    打开你的

    VS.net

    ,新建一个控制台应用程序(

    Console 

    Application

    ,下面这些代码将让你体味到完全控制

    一个线程的无穷乐趣! 

    ThreadTest.cs 

    using System; 

    using System.Threading;  

    namespace ThreadTest 

    {  

    public class Alpha 

    {  

    public void Beta() 

    {  

    while (true)  

    {  

    Console.WriteLine("Alpha.Beta is running in its own thread.");   

    }; 

    public class Simple

    {  

    public static int Main() 

    {  

    Console.WriteLine("Thread Start/Stop/Join Sample"); 

    Alpha oAlpha = new Alpha();  

    file://

    这里创建一个线程,使之执行

    Alpha

    类的

    Beta()

    方法

     

    Thread oThread = new Thread(new ThreadStart(oAlpha.Beta)); 

     

    oThread.Start(); 

     

    while (!oThread.IsAlive); 

     

    Thread.Sleep(1); 

     

     

    oThread.Abort(); 

    oThread.Join(); 

    Console.WriteLine(); 

    Console.WriteLine("Alpha.Beta has finished");

    try 

    Console.WriteLine("Try to restart the Alpha.Beta thread"); 

     

    oThread.Start(); 

     

    }

     

    catch (ThreadStateException) 

     

     

    Console.Write("ThreadStateException trying to restart Alpha.Beta. "); 

     

    Console.WriteLine("Expected since aborted threads cannot be restarted."); 

     

    Console.ReadLine(); 

     

     

    return 0; 

     

    }

      

     

    这段程序包含两个类

    Alpha

    Simple

    ,在创建线程

    oThread

    时我们用指向

    Alpha.Beta()

    方法的初始化了

    ThreadStart

    代理(

    delegate

    )对象,当我们创建的线程

    oThread

    调用

    oThread.Start()

    方法启动时,实际上程序

    运行的是

    Alpha.Beta()

    方法:

     

     

    Alpha oAlpha = new Alpha(); 

     

     

    Thread oThread = new Thread(new ThreadStart(oAlpha.Beta)); 

     

     

    oThread.Start(); 

     

     

     

    然后在

    Main()

    函数的

    while

    循环中,我们使用静态方法

    Thread.Sleep()

    让主线程停了

    1ms

    ,这段时间

    CPU

    向执行线程

    oThread

    。然后我们试图用

    Thread.Abort()

    方法终止线程

    oThread

    ,注意后面的

    oThread.Join()

    Thread.Join()

    方法使主线程等待,

    直到

    oThread

    线程结束。

    你可以给

    Thread.Join()

    方法指定一个

    int

    型的参数

    作为等待的最长时间。之后,我们试图用

    Thread.Start()

    方法重新启动线程

    oThread

    ,但是显然

    Abort()

    方法

    带来的后果是不可恢复的终止线程,所以最后程序会抛出

    ThreadStateException

    异常。

     

     

     

    程序最后得到的结果将如下图:

     

     

    在这里我们要注意的是其它线程都是依附于

    Main()

    函数所在的线程的,

    Main()

    函数是

    C#

    程序的入口,起始

    线程可以称之为主线程,如果所有的前台线程都停止了,那么主线程可以终止,而所有的后台线程都将无

    条件终止。而所有的线程虽然在微观上是串行执行的,但是在宏观上你完全可以认为它们在并行执行。

     

     

     

    读者一定注意到了

    Thread.ThreadState

    这个属性,

    这个属性代表了线程运行时状态,

    在不同的情况下有不同

    的值,

    于是我们有时候可以通过对该值的判断来设计程序流程。

    ThreadState

    在各种情况下的可能取值如下:

     

     

    Aborted

    :线程已停止

     

     

    AbortRequested

    :线程的

    Thread.Abort()

    方法已被调用,但是线程还未停止

     

     

    Background

    :线程在后台执行,与属性

    Thread.IsBackground

    有关

     

     

    Running

    :线程正在正常运行

     

     

    Stopped

    :线程已经被停止

     

     

    StopRequested

    :线程正在被要求停止

     

     

    Suspended

    :线程已经被挂起(此状态下,可以通过调用

    Resume()

    方法重新运行)

     

     

    SuspendRequested

    :线程正在要求被挂起,但是未来得及响应

     

     

    Unstarted

    :未调用

    Thread.Start()

    开始线程的运行

     

     

    WaitSleepJoin

    :线程因为调用了

    Wait(),Sleep()

    Join()

    等方法处于封锁状态

     

     

    上面提到了

    Background

    状态表示该线程在后台运行,

    那么后台运行的线程有什么特别的地方呢?其实后台

    线程跟前台线程只有一个区别,那就是

    后台线程不妨碍程序的终止

    。一旦一个进程所有的前台线程都终止

    后,

    CLR

    (通用语言运行环境)将通过调用任意一个存活中的后台进程的

    Abort()

    方法来彻底终止进程。

     

     

     

    当线程之间争夺

    CPU

    时间时,

    CPU

    按照是线程的优先级给予服务的。在

    C#

    应用程序中,用户可以设定

    5

    个不同的优先级,由高到低分别是

    Highest

    AboveNormal

    Normal

    BelowNormal

    Lowest

    ,在创建线程

    时如果不指定优先级,那么系统默认为

    ThreadPriority.Normal

    。给一个线程指定优先级

     

    ,我们可以使用如下代码:

     

     

    //

    设定优先级为最低

     

    myThread.Priority=ThreadPriority.Lowest; 

     

     

     

    通过设定线程的优先级,我们可以安排一些相对重要的线程优先执行,例如对用户的响应等等。

     

     

     

    现在我们对怎样创建和控制一个线程已经有了一个初步的了解,下面我们将深入研究线程实现中比较典型

    的的问题,并且探讨其解决方法。

     

     

     

     

     

    .

    线程的同步和通讯——生产者和消费者

     

     

     

    假设这样一种情况,两个线程同时维护一个队列,如果一个线程对队列中添加元素,而另外一个线程从队

    列中取用元素,那么我们称添加元素的线程为生产者,称取用元素的线程为消费者。生产者与消费者问题

    看起来很简单,但是却是多线程应用中一个必须解决的问题,它涉及到线程之间的同步和通讯问题。

     

     

     

    前面说过,每个线程都有自己的资源,但是代码区是共享的,即每个线程都可以执行相同的函数。但是多

    线程环境下,可能带来的问题就是几个线程同时执行一个函数,导致数据的混乱,产生不可预料的结果,

    因此我们必须避免这种情况的发生。

    C#

    提供了一个关键字

    lock

    ,它可以把一段代码定义为互斥段(

    critical 

    section

    ,互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待。在

    C#

    中,关键字

    lock

    义如下:

     

     

     

     

    lock(expression) statement_block 

     

     

    expression

    代表你希望跟踪的对象,

    通常是对象引用。

    一般地,

    如果你想保护一个类的实例,

    你可以使用

    this

    如果你希望保护一个静态变量(如互斥代码段在一个静态方法内部)

    ,一般使用类名就可以了。而

    statement_block

    就是互斥段的代码,这段代码在一个时刻内只可能被一个线程执行。

     

     

     

     

     

    下面是一个使用

    lock

    关键字的典型例子,我将在注释里向大家说明

    lock

    关键字的用法和用途:

     

     

    //lock.cs 

    using System; 

    using System.Threading; 

     

     

    internal class Account 

     

     

     

    int balance; 

     

     

    Random r = new Random(); 

     

     

    internal Account(int initial) 

     

     

     

     

     

     

    balance = initial; 

     

     

     

     

     

     

    internal int Withdraw(int amount) 

     

     

     

     

     

     

    if (balance < 0) 

     

     

     

     

     

     

     

     

    file://

    如果

    balance

    小于

    0

    则抛出异常

     

     

     

     

     

    throw new Exception("Negative Balance"); 

     

     

     

     

     

     

    //

    下面的代码保证在当前线程修改

    balance

    的值完成之前

     

     

     

     

    //

    不会有其他线程也执行这段代码来修改

    balance

    的值

     

     

     

     

    //

    因此,

    balance

    的值是不可能小于

    0

     

     

     

     

    lock (this) 

     

     

     

     

     

     

     

    Console.WriteLine("Current Thread:"+Thread.CurrentThread.Name); 

     

     

     

     

    file://

    如果没有

    lock

    关键字的保护,那么可能在执行完

    if

    的条件判断之后

     

     

     

     

     

    file://

    另外一个线程却执行了

    balance=balance-amount

    修改了

    balance

    的值

     

     

     

     

     

    file://

    而这个修改对这个线程是不可见的,所以可能导致这时

    if

    的条件已经不成立了

     

     

     

     

     

    file://

    但是,这个线程却继续执行

    balance=balance-amount

    ,所以导致

    balance

    可能小于

     

     

     

     

    if (balance >= amount) 

     

     

     

     

     

     

     

     

     

     

    Thread.Sleep(5); 

     

     

     

     

     

    balance = balance - amount; 

     

     

     

     

     

    return amount; 

     

     

     

     

     

     

     

     

     

    else 

     

     

     

     

     

     

     

     

     

     

    return 0; // transaction rejected 

     

     

     

     

     

     

     

     

     

     

     

    internal void DoTransactions() 

     

     

     

     

     

     

    for (int i = 0; i < 100; i++) 

     

     

     

     

     

    Withdraw(r.Next(-50, 100)); 

     

     

     

     

    internal class Test 

     

     

     

    static internal Thread[] threads = new Thread[10]; 

     

     

    public static void Main() 

     

     

     

     

     

     

    Account acc = new Account (0); 

     

     

     

    for (int i = 0; i < 10; i++) 

     

     

     

     

     

     

     

     

    Thread t = new Thread(new ThreadStart(acc.DoTransactions)); 

     

     

     

     

    threads[i] = t; 

     

     

     

     

     

     

    for (int i = 0; i < 10; i++) 

     

     

     

     

     

    threads[i].Name=i.ToString(); 

     

     

     

    for (int i = 0; i < 10; i++) 

     

     

     

     

     

    threads[i].Start(); 

     

     

     

    Console.ReadLine(); 

     

     

     

     

     

     

    多线程公用一个对象时,也会出现和公用代码类似的问题,这种问题就不应该使用

    lock

    关键字了,这里

    需要用到

    System.Threading

    中的一个类

    Monitor

    ,我们可以称之为监视器,

    Monitor

    提供了使线程共享资源

    的方案。

     

     

     

     

     

    Monitor

    类可以锁定一个对象,

    一个线程只有得到这把锁才可以对该对象进行操作。

    对象锁机制保证了

    在可能引起混乱的情况下一个时刻只有一个线程可以访问这个对象。

    Monitor

    必须和一个具体的对象相关

    ,但是由于它是一个静态的类,所以不能使用它来定义对象,而且

    它的所有方法都是静态的,不能使用

    对象来引用

    。下面代码说明了使用

    Monitor

    锁定一个对象的情形:

     

     

    ...... 

    Queue oQueue=new Queue(); 

    ...... 

    Monitor.Enter(oQueue); 

    ......//

    现在

    oQueue

    对象只能被当前线程操纵了

     

    Monitor.Exit(oQueue);//

    释放锁

     

     

     

     

    如上所示,

    当一个线程调用

    Monitor.Enter()

    方法锁定一个对象时,

    这个对象就归它所有了,

    其它线程想要访

    问这个对象,只有等待它使用

    Monitor.Exit()

    方法释放锁。为了保证线程最终都能释放锁,你可以把

    Monitor.Exit()

    方法写在

    try-catch-finally

    结构中的

    finally

    代码块里。

    对于任何一个被

    Monitor

    锁定的对象,

    内存中都保存着与它相关的一些信息,其一是现在持有锁的线程的引用,其二是一个预备队列,队列中保

    存了已经准备好获取锁的线程,其三是一个等待队列,队列中保存着当前正在等待这个对象状态改变的队

    列的引用。当拥有对象锁的线程准备释放锁时,它使用

    Monitor.Pulse()

    方法通知等待队列中的第一个线程,

    于是该线程被转移到预备队列中,当对象锁被释放时,在预备队列中的线程可以立即获得对象锁

  • 相关阅读:
    tcpdump 筛选抓包
    TCP拥塞避免
    【转载】TCP协议
    go 参数传递的是值还是引用 (转)
    go 数组指针 指针数组
    go 协程
    go 接口实现
    go函数可见性
    go 继承
    go 结构体函数
  • 原文地址:https://www.cnblogs.com/mr-jia/p/3760743.html
Copyright © 2020-2023  润新知