.NET 中的进程/线程同步技术

来源:互联网 发布:淘宝买家留言 编辑:程序博客网 时间:2024/05/16 05:23

.NET 中的进程/线程同步技术

第1章 线程控制

1.1 线程控制

using System;

using System.Threading;

 

public class Worker

{

    // This method will be called when the thread is started.

    public void DoWork()

    {

        while (!_shouldStop)

        {

            Console.WriteLine("worker thread: working...");

        }

        Console.WriteLine("worker thread: terminating gracefully.");

    }

    public void RequestStop()

    {

        _shouldStop = true;

    }

    // Volatile is used as hint to the compiler that this data

    // member will be accessed by multiple threads.

    private volatile bool _shouldStop;//通过_shouldStop来标识子线程是否退出

}

 

public class WorkerThreadExample

{

    static void Main()

    {

        // Create the thread object. This does not start the thread.

        Worker workerObject = new Worker();

        Thread workerThread = new Thread(workerObject.DoWork);

 

        // Start the worker thread.

        workerThread.Start();//启动线程

        Console.WriteLine("main thread: Starting worker thread...");

 

        // Loop until worker thread activates.

        while (!workerThread.IsAlive);//.IsAlive标示某线程对象是否已激活;被句作用为让当前Main线程一直等到workerThread激活后才继续往下执行。

 

        // Put the main thread to sleep for 1 millisecond to

        // allow the worker thread to do some work:

        Thread.Sleep(1);//当前Main线程sleep 1毫秒

 

        // Request that the worker thread stop itself:

        workerObject.RequestStop();//设置_shouldStop标识,让子线程自己结束

        // workerThread.Abort();Abort 从另一个线程中终止某个线程。这将强行终止受影响的线程,//即使该线程尚未完成其任务,并且未提供清理资源的机会。

        // Use the Join method to block the current thread

        // until the object's thread terminates.

        workerThread.Join();//阻塞当前Main进程,知道指定的workerThread进程停止为止

        Console.WriteLine("main thread: Worker thread has terminated.");

    }

}

1.2 线程池

 

第1章 进程/线程同步技术

1.1 Lock

lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断。这是通过在代码块运行期间为给定对象获取互斥锁来实现的。

lock 语句以关键字 lock 开头,它有一个作为参数的对象,在该参数的后面还有一个一次只能由一个线程执行的代码块。例如:

public class TestThreading

{

    private System.Object lockThis = new System.Object();

 

    public void Function()

    {

        lock (lockThis)

        {

            // Access thread-sensitive resources.

        }

    }

}

QueueUserWorkItem方法

using System;

using System.Threading;

public class Example {

    public static void Main() {

        // Queue the task.

        ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc));

       

        Console.WriteLine("Main thread does some work, then sleeps.");

        // If you comment out the Sleep, the main thread exits before

        // the thread pool task runs.  The thread pool uses background

        // threads, which do not keep the application running.  (This

        // is a simple example of a race condition.)

        Thread.Sleep(1000);

 

        Console.WriteLine("Main thread exits.");

    }

 

    // This thread procedure performs the task.

    static void ThreadProc(Object stateInfo) {

        // No state object was passed to QueueUserWorkItem, so

        // stateInfo is null.

        Console.WriteLine("Hello from the thread pool.");

    }

}

RegisterWaitForSingleObject 方法

using System;

using System.Threading;

 

// TaskInfo holds state information for a task that will be

// executed by a ThreadPool thread.

public class TaskInfo {

    // State information for the task.  These members

    // can be implemented as read-only properties, read/write

    // properties with validation, and so on, as required.

    public string Boilerplate;

    public int Value;

 

    // Public constructor provides an easy way to supply all

    // the information needed for the task.

    public TaskInfo(string text, int number) {

        Boilerplate = text;

        Value = number;

    }

}

 

public class Example {

    public static void Main() {

        // Create an object containing the information needed

        // for the task.

        TaskInfo ti = new TaskInfo("This report displays the number {0}.", 42);

 

        // Queue the task and data.

        if (ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc), ti)) {   

            Console.WriteLine("Main thread does some work, then sleeps.");

 

            // If you comment out the Sleep, the main thread exits before

            // the ThreadPool task has a chance to run.  ThreadPool uses

            // background threads, which do not keep the application

            // running.  (This is a simple example of a race condition.)

            Thread.Sleep(1000);

 

            Console.WriteLine("Main thread exits.");

        }

        else {

            Console.WriteLine("Unable to queue ThreadPool request.");

        }

    }

 

    // The thread procedure performs the independent task, in this case

    // formatting and printing a very simple report.

    //

    static void ThreadProc(Object stateInfo) {

        TaskInfo ti = (TaskInfo) stateInfo;

        Console.WriteLine(ti.Boilerplate, ti.Value);

    }

}

 

1.2 Monitor

lock 关键字类似,监视器防止多个线程同时执行代码块。Enter 方法允许一个且仅一个线程继续执行后面的语句;其他所有线程都将被阻止,直到执行语句的线程调用 Exit。这与使用 lock 关键字一样。事实上,lock 关键字就是用 Monitor 类来实现的。例如:

lock (x)

{

    DoSomething();

}

等效于:

System.Object obj = (System.Object)x;

System.Threading.Monitor.Enter(obj);

try

{

    DoSomething();

}

finally

{

    System.Threading.Monitor.Exit(obj);

}

多于一个线程Enter相同的对象,那么就在同步对象上形成了就绪队列,而当多于一个线程同时Wait相同的对象,就在同步对象上形成了等待队列,每个Pluse释放等待队列头上的单个线程,使它们可以进入就绪队列重新得到锁,要注意,Pluse只是使等待队列中的线程进入就绪队列,如果Pluse的调用线程没有退出它的锁语句(ExitWait),那么进入就绪队列仍然没有办法拥有锁。

Pluse是一个单向通讯,以异步方式进行,不返回任何值来指示这个脉冲是否被接收到了,如果发出脉冲时没有线程在等待队列中,则脉冲会被忽略

提供了PluseAll方法,用于在一刹那之间释放整个等待队列里的线程。

Wait(Object)—释放对象锁后,当前线程进入等待队列,直到pulse将其唤醒,进入就绪队列;

Wait(Object, Int32)/ Wait(Object, TimeSpan)/ Wait(Object, Int32, Boolean),Boolean指定是否在等待前推出上下文同步域然后重新获取该同步域/ Wait(Object, TimeSpan, Boolean)—释放对象锁,直到重新获得锁;此时线程进入等待队列,直到被PulseAll叫醒或被Pulse叫醒(如果此时线程在等待列队对头);若在指定的等待时间内还未被叫醒,在等待列队中的线程就自动移到就绪队列;如果在指定的时间过期之前重新获取该锁,则为 true;如果在指定的时间过期之后重新获取该锁,则为 false。此方法只有在重新获取该锁后才会返回。

注意:如果为 millisecondsTimeout 参数指定了 Infinite,则除非锁的持有者调用 Pulse PulseAll,否则此方法会无限期阻止。如果 millisecondsTimeout 等于零,则调用 Wait 的线程释放锁,然后立即进入就绪队列以便重新获取锁。

调用方执行一次 Wait,与已为指定对象调用 Enter 的次数无关。从概念上说,Wait 方法存储调用方对对象调用 Enter 的次数,并按完全释放锁定对象所需要的次数调用 Exit。然后调用方在等待重新获取对象期间被阻止。当调用方重新获取锁时,系统按还原调用方的已保存 Enter 计数所需要的次数调用 Enter。调用 Wait 仅释放指定对象的锁;如果调用方是其他对象的锁的所有者,则不释放这些锁。

 

1.3 同步事件和等待句柄

使用锁或监视器对于防止同时执行区分线程的代码块很有用,但是这些构造不允许一个线程向另一个线程传达事件

这需要同步事件,它是有两个状态(终止和非终止)的对象,可以用来激活和挂起线程。让线程等待非终止的同步事件可以将线程挂起,将事件状态更改为终止可以将线程激活。如果线程试图等待已经终止的事件,则线程将继续执行,而不会延迟。

同步事件有两种:AutoResetEvent 和 ManualResetEvent。它们之间唯一的不同在于,无论何时,

--只要 AutoResetEvent 激活线程,它的状态将自动从终止变为非终止

--相反,ManualResetEvent 允许它的终止状态激活任意多个线程,只有当它的 Reset 方法被调用时才还原到非终止状态。

可以通过调用一种等待方法,如 WaitOneWaitAny WaitAll,让线程等待事件。WaitHandle..::.WaitOne()()() 使线程一直等待,直到单个事件变为终止状态;WaitHandle..::.WaitAny()()() 阻止线程,直到一个或多个指示的事件变为终止状态;WaitHandle..::.WaitAll()()() 阻止线程,直到所有指示的事件都变为终止状态。当调用事件的 Set 方法时,事件将变为终止状态。

在下面的示例中,创建了一个线程,并由 Main 函数启动该线程。新线程使用 WaitOne 方法等待一个事件。在该事件被执行 Main 函数的主线程终止之前,该线程一直处于挂起状态。一旦该事件终止,辅助线程将返回。在本示例中,因为事件只用于一个线程的激活,所以使用 AutoResetEvent ManualResetEvent 类都可以。

using System;

using System.Threading;

 

class ThreadingExample

{

    static AutoResetEvent autoEvent;

 

    static void DoWork()

    {

        Console.WriteLine("   worker thread started, now waiting on event...");

        autoEvent.WaitOne();

        Console.WriteLine("   worker thread reactivated, now exiting...");

    }

 

    static void Main()

    {

        autoEvent = new AutoResetEvent(false);

 

        Console.WriteLine("main thread starting worker thread...");

        Thread t = new Thread(DoWork);

        t.Start();

 

        Console.WriteLine("main thread sleeping for 1 second...");

        Thread.Sleep(1000);

 

        Console.WriteLine("main thread signaling worker thread...");

        autoEvent.Set();

    }

}

AutoResetEvent 类

AutoResetEvent 允许线程通过发信号互相通信。通常,此通信涉及线程需要独占访问的资源。

线程通过调用 AutoResetEvent 上的 WaitOne 来等待信号。如果 AutoResetEvent 处于非终止状态,则该线程阻塞,并等待当前控制资源的线程通过调用 Set 发出资源可用的信号。

调用 Set AutoResetEvent 发信号以释放等待线程AutoResetEvent 将保持终止状态,直到一个正在等待的线程被释放,然后自动返回非终止状态。如果没有任何线程在等待,则状态将无限期地保持为终止状态。

可以通过将一个布尔值传递给构造函数来控制 AutoResetEvent 的初始状态,如果初始状态为终止状态,则为 true;否则为 false

AutoResetEvent 也可以同 staticWaitAll WaitAny 方法一起使用。

有关线程同步机制的更多信息,请参见概念文档中的 AutoResetEvent

- set()将Event设为中止状态

--reset() 将Event设为非中止状态

ManualResetEvent 类

ManualResetEvent 允许线程通过发信号互相通信。通常,此通信涉及一个线程在其他线程进行之前必须完成的任务。

当一个线程开始一个活动(此活动必须完成后,其他线程才能开始)时,它调用 Reset 以将 ManualResetEvent 置于非终止状态。此线程可被视为控制 ManualResetEvent。调用 ManualResetEvent 上的 WaitOne 的线程将阻止,并等待信号。当控制线程完成活动时,它调用 Set 以发出等待线程可以继续进行的信号。并释放所有等待线程。

一旦它被终止,ManualResetEvent 将保持终止状态,直到它被手动重置。即对 WaitOne 的调用将立即返回。

可以通过将布尔值传递给构造函数来控制 ManualResetEvent 的初始状态,如果初始状态处于终止状态,为 true;否则为 false

ManualResetEvent 也可以同 staticWaitAll WaitAny 方法一起使用。

有关线程同步机制的更多信息,请参见概念文档中的 ManualResetEvent

using System;

using System.Threading;

 

class CalculateTest

{

    static void Main()

    {

        Calculate calc = new Calculate();

        Console.WriteLine("Result = {0}.",

            calc.Result(234).ToString());

        Console.WriteLine("Result = {0}.",

            calc.Result(55).ToString());

    }

}

 

class Calculate

{

    double baseNumber, firstTerm, secondTerm, thirdTerm;

    AutoResetEvent[] autoEvents;

    ManualResetEvent manualEvent;

 

    // Generate random numbers to simulate the actual calculations.

    Random randomGenerator;

 

    public Calculate()

    {

        autoEvents = new AutoResetEvent[]

        {

            new AutoResetEvent(false),

            new AutoResetEvent(false),

            new AutoResetEvent(false)

        };

 

        manualEvent = new ManualResetEvent(false);

    }

 

    void CalculateBase(object stateInfo)

    {

        baseNumber = randomGenerator.NextDouble();

 

        // Signal that baseNumber is ready.

        manualEvent.Set();

    }

 

    // The following CalculateX methods all perform the same

    // series of steps as commented in CalculateFirstTerm.

 

    void CalculateFirstTerm(object stateInfo)

    {

        // Perform a precalculation.

        double preCalc = randomGenerator.NextDouble();

 

        // Wait for baseNumber to be calculated.

        manualEvent.WaitOne();

 

        // Calculate the first term from preCalc and baseNumber.

        firstTerm = preCalc * baseNumber *

            randomGenerator.NextDouble();

 

  • .NET 中的进程/线程同步技术
  • Java中的线程同步技术
  • Symbian中的线程、进程及同步
  • 进程,线程,线程同步
  • Unix/linux进程及线程间同步技术总结
  • 进程同步和线程同步
  • 进程同步和线程同步
  • 进程/线程间同步
  • 线程/进程同步问题
  • 进程/线程间同步
  • 进程线程同步相关
  • 线程进程/同步异步
  • 进程/线程 同步机制
  • 进程线程间同步
  • 进程线程同步机制
  • 线程 进程 同步 通信
  • 进程线程同步
  • 进程/线程同步机制
  • LCD 调试总结(ZZ加实践)
  • Sql Server通用的分页存储过程
  • Java内存管理机制与GC
  • 用sqlyog导入外部文件
  • 正则表达式之匹配关系
  • .NET 中的进程/线程同步技术
  • 《精通CSS高级WEB标准解决方案》第一章:基础知识
  • 让VC++直接生成汇编代码
  • linux协议栈之链路层上的数据传输之网卡驱动
  • FD_SET等重定义问题58个编译错误
  • sftp server环境搭建
  • 生成随机密码
  • VC++ 6.0实用技巧汇总
  • Windows平台的ruby IDE 点评
  • 原创粉丝点击