多线程(二)

简易阻止:Sleep,阻止给定的时间周期;Join,等待另一个线程完成。

一旦被阻止,线程立刻放弃分配给他的CPU资源,并且ThreadState属性值改为WaitSleepJoin。

停止阻止的方式:(1)操作超时(2)通过Thread的Interrupt中断了(3)通过Thread的Abort放弃了.

Thread.Sleep(Timeout.Infinite);//休眠直到中断

            Thread.Sleep(TimeSpan.FromSeconds(1));//休眠一秒

            Thread.Sleep(0);//暂时的放弃CPU,时间刚刚够其他时间片里的线程执行。

Join方法也接收一个使用毫秒或用TimeSpan类的超时参数,当Join超时时返回false,如果线程已终止,则返回true。(在阻止是Join保持信息汲取,sleep停止信息汲取)。

 

锁系统:lock,确保只有一个线程访问某资源,不能跨进程,速度快;

        Mutex, ,确保只有一个线程访问某资源,可以跨进程,速度中等;

        Semaphore,不超过制定数目的线程访问某资源,可以跨进程,速度中等。

 

任何对所有有关系的线程都可见的对象都可以作为同步对象,但要服从一个硬性规定:它必须是引用类型。也强烈建议同步对象最好私有在类里面(比如一个私有实例字段)防止无意间从外部锁定相同的对象。服从这些规则,同步对象可以兼对象和保护两种作用。

List<string> list = new List<string>();

        void Test()

        {

            lock (list)

            {

                list.Add("");

                //...

            }

        }

锁并没有以任何方式阻止对同步对象本身的访问,换言之,x.ToString()不会由于另一个线程调用lock(x) 而被阻止,两者都要调用lock(x) 来完成阻止工作。

 

Interrupt 和 Abort

static void Main(string[] args)

        {

            Thread t = new Thread(

                delegate()

                {

                    try

                    {

                        Thread.Sleep(Timeout.Infinite);

                    }

                    catch (ThreadInterruptedException)

                    {

                        Console.WriteLine("Interrupt!");

                    }

                    Console.WriteLine("finally");

                }

                );

            t.Start();

            t.Interrupt();

            Console.Read();

        }

        //Interrupt!

        //finally

Interrupt中断一个线程仅仅释放其当前的等待状态,并不结束这个线程,后面该干啥干啥!

 

被阻止的线程也可以通过Abort方法被强制释放,这与调用Interrupt相似,除了用ThreadAbortException异常代替了ThreadInterruptedException异常,此外,异常将被重新抛出在catch里(在试图以有好方式处理异常的时候),直到Thread.ResetAbort在catch中被调用;在这期间线程的ThreadState为AbortRequested。

在Interrupt 与 Abort 之间最大不同在于它们调用一个非阻止线程所发生的事情。Interrupt继续工作直到下一次阻止发生,Abort在线程当前所执行的位置(可能甚至不在你的代码中)抛出异常。

 //当两个或更多线程需要同时访问一个共享资源时,系统需要使用同步机制来确保一次只有一个线程使用该资源。Mutex 是同步基元,它只向一个线程授予对共享资源的独占访问权。如果一个线程获取了互斥体,则要获取该互斥体的第二个线程将被挂起,直到第一个线程释放该互斥体。
    //可以使用 WaitHandle.WaitOne 方法请求互斥体的所属权。拥有互斥体的线程可以在对 WaitOne 的重复调用中请求相同的互斥体而不会阻止其执行。但线程必须调用 ReleaseMutex 方法同样多的次数以释放互斥体的所属权。Mutex 类强制线程标识,因此互斥体只能由获得它的线程释放。相反,Semaphore 类不强制线程标识。 
    //互斥体有两种类型:局部互斥体和已命名的系统互斥体。如果使用接受名称的构造函数创建 Mutex 对象,则该对象与具有该名称的操作系统对象关联。已命名的系统互斥体在整个操作系统中都可见,可用于同步进程活动。您可以创建多个 Mutex 对象来表示同一个已命名的系统互斥体,也可以使用 OpenExisting 方法打开现有的已命名系统互斥体。(msdn)

    class TreadSafe
    {
        private static Mutex mut = new Mutex();//可以在构造函数中命名
        private const int nums = 3;
        private const int arrs = 1;

        static void Main(string[] args)
        {
            for (int i = 0; i < nums; i++)
            {
                Thread t = new Thread(Go);
                t.Name = string.Format("Thread{0}", i);
                t.Start();
            }
            Console.Read();
        }

        static void Go()
        {
            for (int i = 0; i < arrs; i++)
            {
                useResource();
            }
        }

        private static void useResource()
        {
            mut.WaitOne();
            Console.WriteLine(string.Format("{0}进入", Thread.CurrentThread.Name));
            Thread.Sleep(100);
            Console.WriteLine(string.Format("{0}离开", Thread.CurrentThread.Name));
            mut.ReleaseMutex();
        }
    }
  /*使用 Semaphore 类可控制对资源池的访问。线程通过调用 WaitOne 方法(从 WaitHandle 类继承)进入信号量,并通过调用 Release 方法释放信号量。
信号量的计数在每次线程进入信号量时减小,在线程释放信号量时增加。当计数为零时,后面的请求将被阻塞,直到有其他线程释放信号量。当所有的线程都已释放信号量时,计数达到创建信号量时所指定的最大值。
被阻止的线程并不一定按特定的顺序(如 FIFO 或 LIFO)进入信号量。
线程可通过重复调用 WaitOne 方法多次进入信号量。为释放这些入口中的部分或全部,线程可多次调用无参数的 Release 方法重载,也可以调用 Release(Int32) 方法重载来指定要释放的入口数。
Semaphore 类不对 WaitOne 或 Release 调用强制线程标识。程序员负责确保线程释放信号量的次数不能太多。例如,假定信号量的最大计数为 2,并且线程 A 和线程 B 同时进入信号量。如果线程 B 中的编程错误导致它两次调用 Release,则两次调用都成功。这样,信号量的计数已满,当线程 A 最终调用 Release 时便会引发 SemaphoreFullException。
信号量分为两种类型:局部信号量和已命名的系统信号量。如果您使用接受名称的构造函数创建 Semaphore 对象,则该对象与具有该名称的操作系统信号量关联。已命名的系统信号量在整个操作系统中都可见,可用于同步进程活动。您可以创建多个 Semaphore 对象来表示同一个已命名的系统信号量,也可以使用 OpenExisting 方法打开现有的已命名系统信号量。
局部信号量仅存在于您的进程内。您的进程中任何引用局部 Semaphore 对象的线程都可以使用它。每个 Semaphore 对象都是一个单独的局部信号量。*/
    class Program
    {
        private static Semaphore _pool;
        private static int _padding;
        static void Main(string[] args)
        {
            _pool = new Semaphore(0, 3);
            for (int i = 1; i <= 5; i++)
            {
                Thread t = new Thread(new ParameterizedThreadStart(Go));
                t.Start(i);
            }
            Thread.Sleep(500);
            Console.WriteLine("主线程将信号量增加到3");
            _pool.Release(3);
            Console.Read();
        }

        static void Go(object num)
        {
            Console.WriteLine("Thread{0}开始,并等待Semaphore", num);
            _pool.WaitOne();

            int padding = Interlocked.Add(ref _padding, 100);//原子操作,两个值相加,把第一个值更新为两值和
            Console.WriteLine("Thread{0}进入Semaphore", num);

            Thread.Sleep(1000 + padding);
            Console.WriteLine("Thread{0} 离出 semaphore.", num);
            Console.WriteLine("Thread{0} 前一个信号量数: {1}", num, _pool.Release());
        }
    }

 

class Program
    {
        /*AutoResetEvent 允许线程通过发信号互相通信。 通常,当线程需要独占访问资源时使用该类。 
线程通过调用 AutoResetEvent 上的 WaitOne 来等待信号。 如果 AutoResetEvent 为非终止状态,则线程会被阻止,并等待当前控制资源的线程通过调用 Set 来通知资源可用。 
调用 Set 向 AutoResetEvent 发信号以释放等待线程。 AutoResetEvent 将保持终止状态,直到一个正在等待的线程被释放,然后自动返回非终止状态。 如果没有任何线程在等待,则状态将无限期地保持为终止状态。 
如果当 AutoResetEvent 为终止状态时线程调用 WaitOne,则线程不会被阻止。 AutoResetEvent 将立即释放线程并返回到非终止状态。*/
        static AutoResetEvent wh = new AutoResetEvent(false);
        static void Main()
        {
            new Thread(Go).Start();
            Thread.Sleep(2000);
            wh.Set();//释放
            Console.Read();
        }

        static void Go()
        {
            Console.WriteLine("waitting..........");
            wh.WaitOne();//暂停
            Console.WriteLine("OK");
        }
    }
 //生产者/消费者队列
    class ProducerCustomerQueue : IDisposable
    {
        AutoResetEvent wh = new AutoResetEvent(false);
        Thread worker;
        object locker = new object();
        Queue<string> tasks = new Queue<string>();

        public ProducerCustomerQueue()
        {
            worker = new Thread(work);
            worker.Start();
        }

        public void EnqueueTask(string task)
        {
            lock (locker)
            {
                tasks.Enqueue(task);
            }
            wh.Set();
        }

        void work()
        {
            while (true)
            {
                string task = null;
                lock (locker)
                {
                    if (tasks.Count > 0)
                    {
                        task = tasks.Dequeue();
                        if (task == null) //tag
                        {
                            return;
                        }
                    }
                    if (task != null)
                    {
                        Console.WriteLine("执行任务" + task);
                        Thread.Sleep(1000);
                    }
                    else
                    {
                        wh.WaitOne();//没有任务了,等待信号
                    }
                }
            }
        }

        #region IDisposable 成员

        public void Dispose()
        {
            EnqueueTask(null);//goto:tag.告诉消费者退出
            worker.Join();//等待消费者线程完成
            wh.Close();//释放所有资源
        }

        #endregion
    }

    class TreadSafe
    {
        static void Main(string[] args)
        {
            using (ProducerCustomerQueue q = new ProducerCustomerQueue())
            {
                q.EnqueueTask("hello");
                for (int i = 0; i < 10; i++)
                {
                    q.EnqueueTask("say " + i);
                }
                q.EnqueueTask("Bye");
            }
            Console.Read();
        }
    }

 

posted @ 2013-08-07 14:15  hometown  阅读(233)  评论(0编辑  收藏  举报