데이터 베이스 MySQL 튜토리얼 POSIX 线程详解,第2部分

POSIX 线程详解,第2部分

Jun 07, 2016 pm 03:42 PM
posix ~로 이어지다 이상 기사 상해

在 前一篇文章中 ,谈到了会导致异常结果的线程代码。两个线程分别对同一个全局变量进行了二十次加一。变量的最后应该是 40,但最终却是 21。这是怎么回事呢?因为一个线程不停地“取消”了另一个线程执行的加一操作,所以产生这个问题。现在让我们来查看改

在 前一篇文章中 ,谈到了会导致异常结果的线程代码。两个线程分别对同一个全局变量进行了二十次加一。变量的值最后应该是 40,但最终值却是 21。这是怎么回事呢?因为一个线程不停地“取消”了另一个线程执行的加一操作,所以产生这个问题。现在让我们来查看改正后的代码,它使用互斥对象(mutex)来解决该问题:


thread3.c
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int myglobal;
pthread_mutex_t mymutex=PTHREAD_MUTEX_INITIALIZER;
 void *thread_function(void *arg) {
  int i,j;
  for ( i=0; i
</stdio.h></unistd.h></stdlib.h></pthread.h>
로그인 후 복사


解读一下

如果将这段代码与 前一篇文章 中给出的版本作一个比较,就会注意到增加了 pthread_mutex_lock() 和 pthread_mutex_unlock() 函数调用。在线程程序中这些调用执行了不可或缺的功能。他们提供了一种相互排斥的方法(互斥对象即由此得名)。两个线程不能同时对同一个互斥对象加锁。

互斥对象是这样工作的。如果线程 a 试图锁定一个互斥对象,而此时线程 b 已锁定了同一个互斥对象时,线程 a 就将进入睡眠状态。一旦线程 b 释放了互斥对象(通过 pthread_mutex_unlock() 调用),线程 a 就能够锁定这个互斥对象(换句话说,线程 a 就将从 pthread_mutex_lock() 函数调用中返回,同时互斥对象被锁定)。同样地,当线程 a 正锁定互斥对象时,如果线程 c 试图锁定互斥对象的话,线程 c 也将临时进入睡眠状态。对已锁定的互斥对象上调用 pthread_mutex_lock() 的所有线程都将进入睡眠状态,这些睡眠的线程将“排队”访问这个互斥对象。

通常使用 pthread_mutex_lock() 和 pthread_mutex_unlock() 来保护数据结构。这就是说,通过线程的锁定和解锁,对于某一数据结构,确保某一时刻只能有一个线程能够访问它。可以推测到,当线程试图锁定一个未加锁的互斥对象时,POSIX 线程库将同意锁定,而不会使线程进入睡眠状态。


请看这幅轻松的漫画,四个小精灵重现了最近一次 pthread_mutex_lock() 调用的一个场面。
POSIX 线程详解,第2部分

图中,锁定了互斥对象的线程能够存取复杂的数据结构,而不必担心同时会有其它线程干扰。那个数据结构实际上是“冻结”了,直到互斥对象被解锁为止。pthread_mutex_lock() 和 pthread_mutex_unlock() 函数调用,如同“在施工中”标志一样,将正在修改和读取的某一特定共享数据包围起来。这两个函数调用的作用就是警告其它线程,要它们继续睡眠并等待轮到它们对互斥对象加锁。当然,除非在每个 对特定数据结构进行读写操作的语句前后,都分别放上 pthread_mutex_lock() 和 pthread_mutext_unlock() 调用,才会出现这种情况。


为什么要用互斥对象?

听上去很有趣,但究竟为什么要让线程睡眠呢?要知道,线程的主要优点不就是其具有独立工作、更多的时候是同时工作的能力吗?是的,确实是这样。然而,每个重要的线程程序都需要使用某些互斥对象。让我们再看一下示例程序以便理解原因所在。

请看 thread_function(),循环中一开始就锁定了互斥对象,最后才将它解锁。在这个示例程序中,mymutex 用来保护 myglobal 的值。仔细查看 thread_function(),加一代码把 myglobal 复制到一个局部变量,对局部变量加一,睡眠一秒钟,在这之后才把局部变量的值传回给 myglobal。不使用互斥对象时,即使主线程在 thread_function() 线程睡眠一秒钟期间内对 myglobal 加一,thread_function() 苏醒后也会覆盖主线程所加的值。使用互斥对象能够保证这种情形不会发生。(您也许会想到,我增加了一秒钟延迟以触发不正确的结果。把局部变量的值赋给 myglobal 之前,实际上没有什么真正理由要求 thread_function() 睡眠一秒钟。)使用互斥对象的新程序产生了期望的结果:

$ ./thread3
o..o..o.o..o..o.o.o.o.o..o..o..o.ooooooo
myglobal equals 40
로그인 후 복사

为了进一步探索这个极为重要的概念,让我们看一看程序中进行加一操作的代码:

thread_function() 加一代码:
    j=myglobal;
    j=j+1;
    printf(".");
    fflush(stdout);
    sleep(1);
    myglobal=j;
主线程加一代码:
    myglobal=myglobal+1;
로그인 후 복사

如果代码是位于单线程程序中,可以预期 thread_function() 代码将完整执行。接下来才会执行主线程代码(或者是以相反的顺序执行)。在不使用互斥对象的线程程序中,代码可能(几乎是,由于调用了 sleep() 的缘故)以如下的顺序执行:

    thread_function() 线程        主线程
    j=myglobal;
    j=j+1;
    printf(".");
    fflush(stdout);
    sleep(1);                     myglobal=myglobal+1;
    myglobal=j;
로그인 후 복사

当代码以此特定顺序执行时,将覆盖主线程对 myglobal 的修改。程序结束后,就将得到不正确的值。如果是在操纵指针的话,就可能产生段错误。注意到 thread_function() 线程按顺序执行了它的所有指令。看来不象是 thread_function() 有什么次序颠倒。问题是,同一时间内,另一个线程对同一数据结构进行了另一个修改。


线程内幕 1

在解释如何确定在何处使用互斥对象之前,先来深入了解一下线程的内部工作机制。请看第一个例子:

假设主线程将创建三个新线程:线程 a、线程 b 和线程 c。假定首先创建线程 a,然后是线程 b,最后创建线程 c。

    pthread_create( &thread_a, NULL, thread_function, NULL);
    pthread_create( &thread_b, NULL, thread_function, NULL);
    pthread_create( &thread_c, NULL, thread_function, NULL);
로그인 후 복사

在第一个 pthread_create() 调用完成后,可以假定线程 a 不是已存在就是已结束并停止。第二个 pthread_create() 调用后,主线程和线程 b 都可以假定线程 a 存在(或已停止)。

然而,就在第二个 create() 调用返回后,主线程无法假定是哪一个线程(a 或 b)会首先开始运行。虽然两个线程都已存在,线程 CPU 时间片的分配取决于内核和线程库。至于谁将首先运行,并没有严格的规则。尽管线程 a 更有可能在线程 b 之前开始执行,但这并无保证。对于多处理器系统,情况更是如此。如果编写的代码假定在线程 b 开始执行之前实际上执行线程 a 的代码,那么,程序最终正确运行的概率是 99%。或者更糟糕,程序在您的机器上 100% 地正确运行,而在您客户的四处理器服务器上正确运行的概率却是零。

从这个例子还可以得知,线程库保留了每个单独线程的代码执行顺序。换句话说,实际上那三个 pthread_create() 调用将按它们出现的顺序执行。从主线程上来看,所有代码都是依次执行的。有时,可以利用这一点来优化部分线程程序。例如,在上例中,线程 c 就可以假定线程 a 和线程 b 不是正在运行就是已经终止。它不必担心存在还没有创建线程 a 和线程 b 的可能性。可以使用这一逻辑来优化线程程序。


线程内幕 2

现在来看另一个假想的例子。假设有许多线程,他们都正在执行下列代码:

    myglobal=myglobal+1;
로그인 후 복사

那么,是否需要在加一操作语句前后分别锁定和解锁互斥对象呢?也许有人会说“不”。编译器极有可能把上述赋值语句编译成一条机器指令。大家都知道,不可能"半途"中断一条机器指令。即使是硬件中断也不会破坏机器指令的完整性。基于以上考虑,很可能倾向于完全省略 pthread_mutex_lock() 和 pthread_mutex_unlock() 调用。不要这样做。

我在说废话吗?不完全是这样。首先,不应该假定上述赋值语句一定会被编译成一条机器指令,除非亲自验证了机器代码。即使插入某些内嵌汇编语句以确保加一操作的完整执行――甚至,即使是自己动手写编译器!-- 仍然可能有问题。

答案在这里。使用单条内嵌汇编操作码在单处理器系统上可能不会有什么问题。每个加一操作都将完整地进行,并且多半会得到期望的结果。但是多处理器系统则截然不同。在多 CPU 机器上,两个单独的处理器可能会在几乎同一时刻(或者,就在同一时刻)执行上述赋值语句。不要忘了,这时对内存的修改需要先从 L1 写入 L2 高速缓存、然后才写入主存。(SMP 机器并不只是增加了处理器而已;它还有用来仲裁对 RAM 存取的特殊硬件。)最终,根本无法搞清在写入主存的竞争中,哪个 CPU 将会"胜出"。要产生可预测的代码,应使用互斥对象。互斥对象将插入一道"内存关卡",由它来确保对主存的写入按照线程锁定互斥对象的顺序进行。

考虑一种以 32 位块为单位更新主存的 SMP 体系结构。如果未使用互斥对象就对一个 64 位整数进行加一操作,整数的最高 4 位字节可能来自一个 CPU,而其它 4 个字节却来自另一 CPU。糟糕吧!最糟糕的是,使用差劲的技术,您的程序在重要客户的系统上有可能不是很长时间才崩溃一次,就是早上三点钟就崩溃。David R. Butenhof 在他的《POSIX 线程编程》(请参阅本文末尾的参考资料部分)一书中,讨论了由于未使用互斥对象而将产生的种种情况。


许多互斥对象

如果放置了过多的互斥对象,代码就没有什么并发性可言,运行起来也比单线程解决方案慢。如果放置了过少的互斥对象,代码将出现奇怪和令人尴尬的错误。幸运的是,有一个中间立场。首先,互斥对象是用于串行化存取*共享数据*。不要对非共享数据使用互斥对象,并且,如果程序逻辑确保任何时候都只有一个线程能存取特定数据结构,那么也不要使用互斥对象。

其次,如果要使用共享数据,那么在读、写共享数据时都应使用互斥对象。用 pthread_mutex_lock() 和 pthread_mutex_unlock() 把读写部分保护起来,或者在程序中不固定的地方随机使用它们。学会从一个线程的角度来审视代码,并确保程序中每一个线程对内存的观点都是一致和合适的。为了熟悉互斥对象的用法,最初可能要花好几个小时来编写代码,但是很快就会习惯并且*也*不必多想就能够正确使用它们。


使用调用:初始化

现在该来看看使用互斥对象的各种不同方法了。让我们从初始化开始。在 thread3.c 示例 中,我们使用了静态初始化方法。这需要声明一个 pthread_mutex_t 变量,并赋给它常数 PTHREAD_MUTEX_INITIALIZER:

pthread_mutex_t mymutex=PTHREAD_MUTEX_INITIALIZER;
로그인 후 복사

很简单吧。但是还可以动态地创建互斥对象。当代码使用 malloc() 分配一个新的互斥对象时,使用这种动态方法。此时,静态初始化方法是行不通的,并且应当使用例程 pthread_mutex_init():

int pthread_mutex_init( pthread_mutex_t *mymutex, const pthread_mutexattr_t *attr)
로그인 후 복사

正如所示,pthread_mutex_init 接受一个指针作为参数以初始化为互斥对象,该指针指向一块已分配好的内存区。第二个参数,可以接受一个可选的 pthread_mutexattr_t 指针。这个结构可用来设置各种互斥对象属性。但是通常并不需要这些属性,所以正常做法是指定 NULL。

一旦使用 pthread_mutex_init() 初始化了互斥对象,就应使用 pthread_mutex_destroy() 消除它pthread_mutex_destroy() 接受一个指向 pthread_mutext_t 的指针作为参数,并释放创建互斥对象时分配给它的任何资源。请注意,pthread_mutex_destroy()不会 释放用来存储 pthread_mutex_t 的内存。释放自己的内存完全取决于您。还必须注意一点,pthread_mutex_init() 和 pthread_mutex_destroy() 成功时都返回零。


使用调用:锁定

pthread_mutex_lock(pthread_mutex_t *mutex)
로그인 후 복사

pthread_mutex_lock() 接受一个指向互斥对象的指针作为参数以将其锁定。如果碰巧已经锁定了互斥对象,调用者将进入睡眠状态。函数返回时,将唤醒调用者(显然)并且调用者还将保留该锁。函数调用成功时返回零,失败时返回非零的错误代码。

pthread_mutex_unlock(pthread_mutex_t *mutex)
로그인 후 복사

pthread_mutex_unlock() 与 pthread_mutex_lock() 相配合,它把线程已经加锁的互斥对象解锁。始终应该尽快对已加锁的互斥对象进行解锁(以提高性能)。并且绝对不要对您未保持锁的互斥对象进行解锁操作(否则,pthread_mutex_unlock() 调用将失败并带一个非零的 EPERM 返回值)。

pthread_mutex_trylock(pthread_mutex_t *mutex)
로그인 후 복사

当线程正在做其它事情的时候(由于互斥对象当前是锁定的),如果希望锁定互斥对象,这个调用就相当方便。调用 pthread_mutex_trylock() 时将尝试锁定互斥对象。如果互斥对象当前处于解锁状态,那么您将获得该锁并且函数将返回零。然而,如果互斥对象已锁定,这个调用也不会阻塞。当然,它会返回非零的 EBUSY 错误值。然后可以继续做其它事情,稍后再尝试锁定。


等待条件发生

互斥对象是线程程序必需的工具,但它们并非万能的。例如,如果线程正在等待共享数据内某个条件出现,那会发生什么呢?代码可以反复对互斥对象锁定和解锁,以检查值的任何变化。同时,还要快速将互斥对象解锁,以便其它线程能够进行任何必需的更改。这是一种非常可怕的方法,因为线程需要在合理的时间范围内频繁地循环检测变化。

在每次检查之间,可以让调用线程短暂地进入睡眠,比如睡眠三秒钟,但是因此线程代码就无法最快作出响应。真正需要的是这样一种方法,当线程在等待满足某些条件时使线程进入睡眠状态。一旦条件满足,还需要一种方法以唤醒因等待满足特定条件而睡眠的线程。如果能够做到这一点,线程代码将是非常高效的,并且不会占用宝贵的互斥对象锁。这正是 POSIX 条件变量能做的事!

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

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

R.E.P.O. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 아무도들을 수없는 경우 오디오를 수정하는 방법
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25 : Myrise에서 모든 것을 잠금 해제하는 방법
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

오늘 Toutiao에 기사를 게시하여 어떻게 돈을 벌 수 있나요? 오늘 Toutiao에 기사를 게시하여 더 많은 수입을 얻는 방법! 오늘 Toutiao에 기사를 게시하여 어떻게 돈을 벌 수 있나요? 오늘 Toutiao에 기사를 게시하여 더 많은 수입을 얻는 방법! Mar 15, 2024 pm 04:13 PM

1. 오늘 Toutiao에 기사를 게시하여 어떻게 돈을 벌 수 있습니까? 오늘 Toutiao에 기사를 게시하여 더 많은 수입을 얻는 방법! 1. 기본 권익 활성화: 기사의 원본은 광고를 통해 수익을 얻을 수 있으며, 동영상은 가로 화면 모드에서 원본이어야 수익을 얻을 수 있습니다. 2. 팬 100명 권리 활성화: 팬 수가 100명 이상에 도달하면 마이크로 헤드라인, 오리지널 Q&A 작성, Q&A 수익을 얻을 수 있습니다. 3. 독창적인 작품을 고집하라: 독창적인 작품에는 기사, 마이크로 헤드라인, 질문 등이 포함되며, 300단어 이상이어야 합니다. 불법 표절 저작물을 원작으로 출판할 경우 크레딧 점수가 차감되며, 수익금도 차감되므로 주의하시기 바랍니다. 4. 수직성: 전문 분야에서 기사를 작성할 때 분야를 넘나들며 마음대로 기사를 작성할 수 없으며 적절한 추천을 받을 수 없으며, 작품의 전문성과 정확성을 확보할 수 없으며 팬을 유치하기가 어렵습니다. 그리고 독자들. 5. 활동: 높은 활동,

Win11에서 관리자 권한을 얻는 방법에 대한 자세한 설명 Win11에서 관리자 권한을 얻는 방법에 대한 자세한 설명 Mar 08, 2024 pm 03:06 PM

Windows 운영 체제는 세계에서 가장 인기 있는 운영 체제 중 하나이며, 새로운 버전의 Win11이 많은 주목을 받았습니다. Win11 시스템에서 관리자 권한을 얻는 것은 사용자가 시스템에서 더 많은 작업과 설정을 수행할 수 있도록 하는 중요한 작업입니다. 이번 글에서는 Win11 시스템에서 관리자 권한을 얻는 방법과 권한을 효과적으로 관리하는 방법을 자세히 소개하겠습니다. Win11 시스템에서 관리자 권한은 로컬 관리자와 도메인 관리자의 두 가지 유형으로 나뉩니다. 로컬 관리자는 로컬 컴퓨터에 대한 모든 관리 권한을 갖습니다.

Oracle SQL의 나누기 연산에 대한 자세한 설명 Oracle SQL의 나누기 연산에 대한 자세한 설명 Mar 10, 2024 am 09:51 AM

OracleSQL의 나눗셈 연산에 대한 자세한 설명 OracleSQL에서 나눗셈 연산은 두 숫자를 나눈 결과를 계산하는 데 사용되는 일반적이고 중요한 수학 연산입니다. 나누기는 데이터베이스 쿼리에 자주 사용되므로 OracleSQL에서 나누기 작업과 사용법을 이해하는 것은 데이터베이스 개발자에게 필수적인 기술 중 하나입니다. 이 기사에서는 OracleSQL의 나누기 작업 관련 지식을 자세히 설명하고 독자가 참고할 수 있는 특정 코드 예제를 제공합니다. 1. OracleSQL의 Division 연산

로닌 라이즈 풀의 특이한 미션 가이드 로닌 라이즈 풀의 특이한 미션 가이드 Mar 26, 2024 pm 08:06 PM

수영장의 이상 현상은 게임의 부수적인 작업입니다. 많은 플레이어들이 수영장 작업의 이상 현상을 완료하는 방법을 알고 싶어합니다. 먼저, 물 속에서의 사격 기술을 익혀야 합니다. 나중에 우리는 수영장 아래에 많은 시체가 있다는 것을 발견했습니다. Rise of Ronin의 수영장에서 특이한 작업에 대한 이 그래픽 가이드를 살펴보겠습니다. Ronin Rise Pool의 특이한 임무 안내: 1. Iizuka와 대화하고 물 속에서 사격하는 기술을 배웁니다. 2. 풀에서 비정상적인 태스크를 받으려면 아래 그림의 위치로 이동하세요. 3. 미션 장소로 이동하여 NPC에게 말을 걸면, 근처 수영장에서 악취가 난다는 것을 알게 됩니다. 4. 조사를 위해 수영장으로 이동합니다. 5. 아래 사진의 위치로 수영하여 수중 다이빙을 하면 시체를 많이 발견하게 됩니다. 6. 카메라를 이용해 시체 사진을 찍어보세요. 7

MIT의 최신 걸작: GPT-3.5를 사용하여 시계열 이상 탐지 문제 해결 MIT의 최신 걸작: GPT-3.5를 사용하여 시계열 이상 탐지 문제 해결 Jun 08, 2024 pm 06:09 PM

오늘은 지난 주 MIT에서 발표한 기사를 소개하고자 합니다. GPT-3.5-turbo를 사용하여 시계열 이상 탐지 문제를 해결하고, 시계열 이상 탐지에서 LLM의 효율성을 초기에 검증한 내용입니다. 전체 과정에 미세한 조정은 없으며, 이상 탐지를 위해 GPT-3.5-turbo를 직접 사용하는 것이 이 글의 핵심이다. LLM이 이상 탐지 작업을 해결하도록 하는 프롬프트 또는 파이프라인입니다. 이 작품을 자세히 소개하겠습니다. 이미지 논문 제목: Large Languagemodelscanbezero-shotanomalydete

PHP 모듈로 연산자의 역할과 사용법에 대한 자세한 설명 PHP 모듈로 연산자의 역할과 사용법에 대한 자세한 설명 Mar 19, 2024 pm 04:33 PM

PHP의 모듈로 연산자(%)는 두 숫자를 나눈 나머지를 구하는 데 사용됩니다. 이 글에서는 모듈로 연산자의 역할과 사용법을 자세히 논의하고 독자의 이해를 돕기 위해 구체적인 코드 예제를 제공합니다. 1. 모듈로 연산자의 역할 수학에서는 정수를 다른 정수로 나누면 몫과 나머지가 나옵니다. 예를 들어 10을 3으로 나누면 몫은 3이고 나머지는 1입니다. 이 나머지를 얻기 위해 모듈로 연산자가 사용됩니다. 2. 모듈러스 연산자의 사용법 PHP에서는 모듈러스를 나타내기 위해 % 기호를 사용합니다.

C++ 함수 예외 및 단일 테스트: 코드 건전성 보장 C++ 함수 예외 및 단일 테스트: 코드 건전성 보장 May 03, 2024 am 09:18 AM

예외 처리 및 단위 테스트는 C++ 코드의 건전성을 보장하는 중요한 방법입니다. 예외는 try-catch 블록을 통해 처리되며, 코드에서 예외가 발생하면 catch 블록으로 이동합니다. 단위 테스트는 코드 테스트를 분리하여 예외 처리가 다양한 상황에서 예상대로 작동하는지 확인합니다. 실제 사례: sumArray 함수는 배열 요소의 합계를 계산하고 빈 입력 배열을 처리하기 위해 예외를 발생시킵니다. 단위 테스트는 배열이 비어 있을 때 std::invalid_argument 예외를 발생시키는 등 비정상적인 상황에서 예상되는 함수 동작을 확인합니다. 결론: 예외 처리 및 단위 테스트를 활용하여 예외를 처리하고, 코드 충돌을 방지하고, 비정상적인 조건에서 코드가 예상대로 작동하는지 확인할 수 있습니다.

C++ 동시 프로그래밍: 스레드 고갈과 우선순위 반전을 방지하는 방법은 무엇입니까? C++ 동시 프로그래밍: 스레드 고갈과 우선순위 반전을 방지하는 방법은 무엇입니까? May 06, 2024 pm 05:27 PM

스레드 부족을 방지하려면 공정한 잠금을 사용하여 리소스를 공정하게 할당하거나 스레드 우선순위를 설정할 수 있습니다. 우선순위 역전 문제를 해결하려면 리소스를 보유한 스레드의 우선순위를 일시적으로 높이는 우선순위 상속을 사용하거나 리소스가 필요한 스레드의 우선순위를 높이는 잠금 승격을 사용할 수 있습니다.

See all articles