>백엔드 개발 >C#.Net 튜토리얼 >0에서 C#12 자율 학습 - 스레드 동기화 솔루션 요약 및 장점과 단점

0에서 C#12 자율 학습 - 스레드 동기화 솔루션 요약 및 장점과 단점

黄舟
黄舟원래의
2017-02-04 11:11:151486검색

우선 한 가지는 확실합니다. Microsoft의 FCL(프레임워크 클래스 라이브러리)은 모든 정적 메서드가 스레드로부터 안전하다는 것을 보장합니다.

FCL은 인스턴스 메서드가 스레드로부터 안전하다고 보장하지 않습니다. 모든 잠금을 추가하면 성능이 크게 저하되기 때문입니다. 또한 각 인스턴스 메서드가 잠금을 획득하고 해제해야 하는 경우 실제로 특정 순간에 애플리케이션에서 단 하나의 스레드만 실행하게 되며 이는 성능에 명백한 영향을 미칩니다.

다음은 기본 스레드 동기화 구성을 소개합니다.

프리미티브: 코드에서 사용할 수 있는 가장 간단한 구성을 참조하세요. 사용자 모드와 커널 모드라는 두 가지 기본 구성이 있습니다.

사용자 모드

는 특수 CPU 명령을 사용하여 스레드를 조정합니다.

기술: 휘발성 키워드, Interlocked 클래스(interlock), 스핀록(spin lock)

공통 잠금 ①: 휘발성 키워드는 동시에 실행되는 여러 스레드에 의해 필드가 수정될 수 있음을 나타냅니다. 휘발성으로 선언된 필드는 컴파일러 최적화의 대상이 아닙니다(단일 스레드로 액세스한다고 가정). 이렇게 하면 필드가 언제든지 최신 값을 제공할 수 있습니다.

연동 클래스: 여러 스레드가 공유하는 변수에 대한 원자적 연산을 제공합니다. . 소위 원자적 작업은 스레드 스케줄링 메커니즘에 의해 중단되지 않는 작업을 의미합니다. 이 작업이 시작되면 중간에 컨텍스트 전환(다른 스레드로 전환) 없이 끝까지 실행됩니다.

공통 잠금 ②: 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 "스핀"을 차지하지 않습니다.

기본 스레드와 관리 스레드를 서로 동기화할 수 있습니다.

동일한 시스템의 서로 다른 프로세스에서 실행되는 스레드를 동기화할 수 있습니다.

승인되지 않은 계정이 접근하는 것을 방지하기 위해 보안 설정을 적용할 수 있습니다.

스레드는 협력 중인 모든 커널 모드 구성을 사용할 수 있을 때까지 또는 세트의 커널 모드 구성을 사용할 수 있을 때까지 차단할 수 있습니다.

커널 모드 구성에서 차단된 스레드는 시간 초과 값을 지정할 수 있습니다. 지정된 시간 내에 원하는 리소스에 액세스할 수 없는 경우 스레드 차단이 해제되고 다른 작업을 수행할 수 있습니다.

단점:

스레드를 사용자 모드에서 커널 모드로(또는 그 반대로) 전환하면 성능이 크게 저하되므로 커널 구성을 피해야 합니다. 또한 스레드가 항상 차단되면 "교착 상태"(교착 상태)가 발생합니다.

교착 상태는 항상 livelock으로 인해 발생합니다. livelock은 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--线程同步解决方法汇总以及优缺点的内容,更多相关内容请关注PHP中文网(www.php.cn)!


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.