C# 线程同步

概念简介

线程同步

  • 当一个线程执行递增和递减操作时,其他线程需要依次次等待,这种问题通常被称为线程同步。
###原子操作
  • 一个操作只占用一个量子的时间,一次就可以完成。
###阻塞状态(内核模式)
  • 讲等待的线程置于阻塞状态,当线程处于阻塞状态时,只会占用尽量少的CPU时间。然而这样就会引入至少一次所谓的上下文切换。上下文切换是指操作系统的线程调度器。该调度器会保存等待的线程的状态,并切换另一个线程,一次恢复等待的线程状态。这需要消耗相当多的资源。然而如果线程要被挂起很长时间,那么这么做是值得的。这种方式又被称为内核模式。因为只有操作系统的内核才能阻止线程使用CPU时间。

    用户模式

  • 万一线程只需要等待一小段时间,最好只是简单的等待,而不用将线程切换到阻塞状态。虽然线程等待依然会浪费CPU时间。但我们节省了上下文切换耗费的CPU时间。该方式被称为用户模式,该方式非常轻量,速度很快,但如果线程需要等待较长时间则会浪费大量的CPU时间。

###混合模式
  • 混合模式是内核模式和用户模式的混合,混合模式先尝试使用用户模式等待,如果线程等待了足够长的时间,则切换到阻塞状态(即内核模式)以节省CPU资源

原子操作



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace testThread_atom
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Incorrect counter");
            var c = new Counter();
            var t1 = new Thread(()=>testcounter(c));
            var t2 = new Thread(() => testcounter(c));
            var t3 = new Thread(() => testcounter(c));
            t1.Start();
            t2.Start();
            t3.Start();
            t1.Join();
            t2.Join();
            t3.Join();
            Console.WriteLine("Total Counter:{0}",c.counter);
            Console.WriteLine("--------------------------");
            Console.WriteLine("Correct counter");

            var c1 = new CounterWithNolock();
            t1 = new Thread(() => testcounter(c1));
            t2 = new Thread(() => testcounter(c1));
            t3 = new Thread(() => testcounter(c1));
            t1.Start();
            t2.Start();
            t3.Start();
            t1.Join();
            t2.Join();
            t3.Join();
            Console.WriteLine("Total Counter:{0}",c1.Counter);
        }
        static void testcounter(CounterBase c)
        {
            for (int i = 0; i < 10000; i++)
            {
                c.Increment();
                c.Decrement();
            }
        }
    }
    abstract class CounterBase
    {
        public abstract void Increment();
        public abstract void Decrement();
    }
    class Counter : CounterBase
    {
        private int _counter;
        public int counter { get { return _counter; } }
        public override void Increment()
        {
            _counter++;
        }
        public override void Decrement()
        {
            _counter--;
        }
    }
    class CounterWithNolock : CounterBase
    {
        private int _counter;
        public int Counter { get { return _counter; } }
        public override void Increment()
        {
            Interlocked.Increment(ref _counter);
        }
        public override void Decrement()
        {
            Interlocked.Decrement(ref _counter);
        }
    }
}

  • 结果

998935-20160728105028575-1262473536.png

使用Mutex类

  • Mutex是一种原始的同步方式。其只是一个对线程授予对共享资源的独自访问。

  • 实例

              static void Main(string[] args)
        {
            const string MutexName = "CsharpThreadingCookBook";
            using(var m=new Mutex(false,MutexName))
            {
                if (!m.WaitOne(TimeSpan.FromSeconds(5), false))
                {
                    Console.WriteLine("Second instance is running");
                    Console.ReadLine();
                }
                else
                {
                    Console.WriteLine("Running!");
                    Console.ReadLine();
                    m.ReleaseMutex();
                }
            }
        }         
  • 结果
    当程序启动时,定义了一个指定名称的互斥量,设置initalOwner标志为false。这意味着如果互斥量已经被创建,则允许程序获取该互斥量。如果没有获取互斥量,程序则简单的显示running,等待直到按下了任何键,然后释放该互斥量并退出。
    如果再运行同样一个程序,则会5S内尝试获取互斥量。如果此时在第一个程序中按下任何键,第二个程序则会开始执行。然而如果保持等待5S第二个程序将无法获取到改互斥量。

    第一个程序没有按键
    998935-20160728142955059-1554816789.png
    第一个程序按键 第二个程序显示依然是running
    998935-20160728143120763-559076779.png

SemaphoreSlim 类

 class Program
    {
        static SemaphoreSlim _semphore = new SemaphoreSlim(2);
        static void Main(string[] args)
        {
            for (int i = 1; i <= 6; i++)
            {
                string threadname = "Thread" + i;
                int secondtowait = 2 + 2 * i;
                var t = new Thread(()=>accessdatabase(threadname,secondtowait));
                t.Start();
            
            }
        }
        static void accessdatabase(string name,int seconds)
        {
            Console.WriteLine("{0} waits access a database",name);
            _semphore.Wait();
            Console.WriteLine("{0} was granted an access to a database",name);
            Thread.Sleep(TimeSpan.FromSeconds(seconds));
            Console.WriteLine("{0} is Completed",name);
            _semphore.Release();
         }
    }
  • 结果
    998935-20160728145401794-39615047.png

使用AutoResetEvent类


   class Program
    {
        private static AutoResetEvent _workevent = new AutoResetEvent(false);
        private static AutoResetEvent _mainevent = new AutoResetEvent(false);
        static void Main(string[] args)
        {
            var t = new Thread(()=>process(10));
            t.Start();

            Console.WriteLine("waiting for another thread to complete work");
            _workevent.WaitOne();
            Console.WriteLine(" first operation is completed");
            Console.WriteLine(" performing an operation on a main thread");
            Thread.Sleep(TimeSpan.FromSeconds(5));
            _mainevent.Set();
            Console.WriteLine("Now running the second operation on a second thread");
            _workevent.WaitOne();
            Console.WriteLine("second operation is completed");
        }
        static void process(int seconds)
        {
            Console.WriteLine("starting a long running work");
            Thread.Sleep(TimeSpan.FromSeconds(seconds));
            Console.WriteLine("work is done");
            _workevent.Set();

            Console.WriteLine("waiting for a main thread to complete its work ");
            _mainevent.WaitOne();
            Console.WriteLine("starting second operation");
            Thread.Sleep(TimeSpan.FromSeconds(seconds));
            Console.WriteLine("work is done");
            _workevent.Set();

        }
    }
  • 结果
![](http://images2015.cnblogs.com/blog/998935/201607/998935-20160728153622934-1588348280.png)

使用ManualResetEventSlim类


    class Program
    {
        static ManualResetEventSlim _mainevent = new ManualResetEventSlim(false);
        static void Main(string[] args)
        {
            var t1 = new Thread(() => TravelThroughGates("Thread 1",5));
            var t2 = new Thread(() => TravelThroughGates("Thread 2", 6));
            var t3 = new Thread(() => TravelThroughGates("Thread 3", 12));
            t1.Start();
            t2.Start();
            t3.Start();
            Thread.Sleep(TimeSpan.FromSeconds(6));

            Console.WriteLine("the gates are now open");
            _mainevent.Set();
            Console.WriteLine("the gates have been closed");
            Thread.Sleep(TimeSpan.FromSeconds(10));
            Console.WriteLine("the gates are now open for second time");
            _mainevent.Set();
            Thread.Sleep(TimeSpan.FromSeconds(2));
            Console.WriteLine("the gates have been closed");
            _mainevent.Reset();
            Console.ReadLine();

        }
        static void TravelThroughGates(string threadname,int seconds)
        {
            Console.WriteLine("{0} fails to sleep",threadname);
            Thread.Sleep(TimeSpan.FromSeconds(seconds));

            Console.WriteLine("{0} waits for the gates to open ",threadname);
            _mainevent.Wait();
            Console.WriteLine("{0} enters the gates",threadname);
        }
    }
  • 结果

998935-20160728170842528-1685082823.png

转载于:https://www.cnblogs.com/ch-lf/p/5713917.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值