旗下导航:搜·么
当前位置:网站首页 > .Net教程 > 正文

从0自学C#12--线程同步解决方法汇总以及优缺点【C#.Net教程】,C#,线程同步,优缺点

作者:搜教程发布时间:2019-11-27分类:.Net教程浏览:26评论:0


导读:起首,一定的一点:Microsoft的FrameworkClassLibrary(FCL)保证了一切静态要领都是线程平安的。FCL不保证实例要领是线程平安的。因为如果悉...
起首,一定的一点:Microsoft的Framework Class Library(FCL)保证了一切静态要领都是线程平安的。

FCL不保证实例要领是线程平安的。因为如果悉数增加锁定,会形成机能的庞大丧失。别的,如果每一个实例要领都须要猎取和开释一个锁,现实上会形成终究在任何给定的时候,你的应用程序只要一个线程在运转,这对机能的影响不言而喻。

下面引见基元线程同步组织。

基元:是指可以在代码中运用的最简朴的组织。有两种基元组织:用户形式(user-mode)和内核形式(kernel-mode)。

用户形式

运用了特别的CPU指令来谐和线程。

手艺:volatile关键字、Interlocked类(互锁)、spinlock(自旋锁)

罕见锁①:volatile 关键字指导一个字段可以由多个同时实行的线程修正。 声明为 volatile 的字段不受编译器优化(假定由单个线程接见)的限定。 如许可以确保该字段在任何时候显现的都是最新的值。

Interlocked类: 为多个线程同享的变量供应原子操纵。。所谓原子操纵是指不会被线程调理机制打断的操纵;这类操纵一旦最先,就一向运转到完毕,中心不会有任何 context switch (切换到另一个线程)。

罕见锁②:SpinLock 构造是一个初级别的互斥同步基元,它在守候猎取锁时举行扭转。在多核盘算机上,当守候时候估计较短且少少涌现争用状况时,SpinLock 的机能将高于其他范例的锁。纵然 SpinLock 未猎取锁,它也会发作线程的时候片。 它如许做是为了防备线程优先级别反转,并使垃圾接纳器可以继承实行。 在运用 SpinLock 时,请确保任何线程持有锁的时候不会凌驾一个非常短的时候段,并确保任何线程在持有锁时不会壅塞。

优点:

应只管运用基元用户形式组织,它们的速率要明显快于内核形式的组织。

谐和线程的在硬件中发作的(所以才这么快)。

然则Microsoft Windows操纵体系永久检测不到一个线程在基元用户形式的组织上壅塞了。

因为在用户形式的基元组织上壅塞的线程池永久不认为已梗塞,所以线程池不会建立新线程来替代这类暂时的线程。

这些CPU指令只壅塞线程相称短的时候。

瑕玷:

只要Windows操纵体系内核才住手一个线程的运转(防备它糟蹋CPU的时候)。

在用户形式中运转的线程能够被体系抢占,但线程会以最快的速率再次调理。

想要获得资本但暂时取不到的线程会一向在用户形式中“自旋”,这能够糟蹋大批的CPU时候。线程一向在一个CPU上运转,我们称为“活锁”(livelock)。

实例:

using System;using System.Threading;public class Worker
{    // This method is 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;
    }    // Keyword volatile is used as a hint to the compiler that this data
    // member is accessed by multiple threads.
    private volatile bool _shouldStop;
}public class WorkerThreadExample
{    static void Main()
    {        // Create the worker 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 the worker thread activates.
        while (!workerThread.IsAlive) ;        // Put the main thread to sleep for 1 millisecond to
        // allow the worker thread to do some work.
        Thread.Sleep(1);        // Request that the worker thread stop itself.
        workerObject.RequestStop();        // Use the Thread.Join method to block the current thread 
        // until the object's thread terminates.
        workerThread.Join();
        Console.WriteLine("Main thread: worker thread has terminated.");
    }    // Sample output:
    // Main thread: starting worker thread...
    // Worker thread: working...
    // Worker thread: working...
    // Worker thread: working...
    // Worker thread: working...
    // Worker thread: working...
    // Worker thread: working...
    // Worker thread: terminating gracefully.
    // Main thread: worker thread has terminated.}

内核形式

由Windows操纵体系本身供应的。它们请求在应用程序的线程中挪用有操纵体系内核完成的函数。

手艺:EventWaitHandle(事宜)、Semaphore(信号量)、Mutex(互斥体)

System.Object  
System.MarshalByRefObject    
System.Threading.WaitHandle      
System.Threading.EventWaitHandle      
System.Threading.Mutex      
System.Threading.Semaphore

罕见锁③:Mutex 类是 Win32 组织的包装,它可以跨应用程序域边境举行封送处置惩罚,可用于多个守候,而且可用于同步差别历程中的线程。

优点:

线程经由过程内核形式的组织猎取其他线程具有的资本时,Windows会壅塞线程以防备它糟蹋CPU时候。当资本变得可用时,Windows会恢复线程,许可它接见资本。它不会占着一个CPU“自旋”。

可完成本机和托管线程相互之间的同步。

可同步在统一台机械的差别历程中运转的线程。

可应用平安性设置,防备未经受权的账户接见它们。

线程可一向壅塞,直到及协作的一切内核形式组织都可用,或许直到鸠合中的任何内核形式组织可用。

在内核形式的组织上壅塞的线程可指定超时价:指定时候内接见不到愿望的资本,线程就可以消除壅塞并实行其他使命。

瑕玷:

将线程从用户形式切换为内核形式(或许相反)会招致庞大的机能丧失,这正是为何要防备运用内核组织的缘由。别的,线程一向壅塞,会致使“死锁“(deadlock)。

死锁老是因为活锁,因为活锁即糟蹋CPU时候,有糟蹋内存(线程栈等),而死锁只糟蹋内存。

夹杂组织

兼具上面二者的优点。在没有合作的状况下,快而且不会壅塞(就像用户形式)。在有合作的状况,愿望它被操纵体系内核壅塞。

手艺:ManualResetEventSlim类、SemaphoreSlim类、Monitor类、Lock类、ReaderWriterLockSlim类、CountdownEvent类、Barrier类、双检锁.

罕见锁④:Monitor 一般更加可取,因为监视器是特地为 .NET Framework 而设想的,因此它比Mutex可以更好地应用资本。只管 mutex 比监视器更加壮大,然则相对于 Monitor 类,它所须要的互操纵转换更斲丧盘算资本。

罕见锁⑤:运用 lock (C#) 或 SyncLock (Visual Basic) 关键字是Monitor的封装。一般比直接运用 Monitor 类更可取,一方面是因为 lock 或 SyncLock 更简约,另一方面是因为lock 或 SyncLock 确保了纵然受庇护的代码激发非常,也可以开释基本监视器。

罕见锁⑥:ReaderWriterLock 锁,在某些状况下,能够愿望只在写入数据时锁定资本,在不更新数据时许可多个客户端同时读取数据。ReaderWriterLock 类在线程修正资本时将强迫其独有接见资本,但在读取资本时则许可非独有接见。 ReaderWriter 锁可用于替代排它锁。运用排它锁时,纵然其他线程不须要更新数据,也会让这些线程守候。

双检锁

罕见锁⑦:两重搜检锁定形式(也被称为”两重搜检加锁优化”,”锁暗示”(Lock hint)) 是一种软件设想形式用来削减并发体系中合作和同步的开支。

两重搜检锁定形式起首考证锁定前提(第一次搜检),只要经由过程锁定前提考证才真正的举行加锁逻辑并再次考证前提(第二次搜检)。

它一般用于削减加锁开支,尤其是为多线程环境中的单例形式完成“惰性初始化”。惰性初始化的意义是直到第一次接见时才初始化它的值。

public sealed class Singleton
    {        private static volatile Singleton instance = null;        
private static object syncRoot = new Object();        
private static int count = 100;        
private Singleton() { }        
public static Singleton Instance
        {            get
            {                if (instance == null)
                {                    lock (syncRoot)
                    {                        if (instance == null)
                            instance = new Singleton();
                    }
                }                return instance;
            }
        }        public static Singleton GetSingleton()
        {            if (instance == null)
            {                lock (syncRoot)
                {                    if (instance == null)
                    {
                        instance = new Singleton();
                    }                        
                }
            }            return instance;
        }        public override string ToString()
        {            lock (syncRoot)
            {                int buf = --count;
                Thread.Sleep(20);                return buf + "_" + count.ToString();
            }
        }
    }static void Main(string[] args)
        {
            Task<string>[] tasks = new Task<string>[10];
            Stopwatch watch = new Stopwatch();            object syncRoot = new Object();
            watch.Start();            for (int i = 0; i < tasks.Length; i++)
            {
                tasks[i] = Task.Factory.StartNew<string>(() =>(Singleton.GetSingleton().ToString()));                    
            }
            Task.WaitAll(tasks, 5000);//设置超时5s
            watch.Stop();
            Console.WriteLine("Tasks running need " + watch.ElapsedMilliseconds + " ms." + "\n");            
for (int i = 0; i < tasks.Length; i++)
            {                //超时处置惩罚
                if (tasks[i].Status != TaskStatus.RanToCompletion)
                {
                    Console.WriteLine("Task {0} Error!", i + 1);
                }                else
                {                    //save result
                    Console.WriteLine("Tick ID is " + tasks[i].Result);
                    Console.WriteLine();
                }
            }            for (int i = 0; i < 3; i++)
            {
                Console.WriteLine("Main thread do work!");
                Thread.Sleep(200);
            }

            Console.ReadKey();
        }

输出:

Tasks running need 298 ms.

Tick ID is 96_96

Tick ID is 99_99

Tick ID is 97_97

Tick ID is 98_98

Tick ID is 95_95

Tick ID is 94_94

Tick ID is 93_93

Tick ID is 92_92

Tick ID is 91_91

Tick ID is 90_90

Main thread do work!
Main thread do work!
Main thread do work!

以上就是从0自学C#12--线程同步解决要领汇总以及优瑕玷的内容,更多相关内容请关注ki4网(www.ki4.cn)!

标签:C#线程同步优缺点


欢迎 发表评论: