寫給本身看的源碼系列: GCD的信號量semaphore

信號量是GCD中最多見的操做,一般用於保證資源的多線程安全性。其本質其實是基於mach內核的信號量接口來實現的,本文將從源碼角度對其進行解析。html

@interface MyObject : NSObject
@property (nonatomic, strong) dispatch_semaphore_t sema;
@end

@implementation MyObject
@end
複製代碼

初始化信號量,而後能夠看到這樣一個結構:ios

myObj.sema = dispatch_semaphore_create(0);
// (lldb) po myObj.sema
// <OS_dispatch_semaphore: semaphore[0x6000007f14f0] = { xref = 1, ref = 1, port = 0x0, value = 0, orig = 0 }>
複製代碼

xref和ref是引用相關的。value和orig則是信號量執行任務的關鍵。執行一次dispatch_semaphore_wait操做後,value值會發生一次減操做。macos

dispatch_semaphore_wait(myObj.sema, DISPATCH_TIME_FOREVER);
// (lldb) po myObj.sema
// <OS_dispatch_semaphore: semaphore[0x60000133b890] = { xref = 2, ref = 1, port = 0x4007, value = -1, orig = 0 }>
複製代碼

那這些成員變量都是什麼意思呢?緩存

dispatch_semaphore_t

信號量的基本數據結構以下:安全

struct dispatch_semaphore_s {
	DISPATCH_OBJECT_HEADER(semaphore);
	long volatile dsema_value;
	long dsema_orig;
	_dispatch_sema4_t dsema_sema;
};
複製代碼
  1. DISPATCH_OBJECT_HEADER(semaphore),GCD中不少對象都有這個header,封裝了一些統一的數據結構。
  2. dsema_orig即爲信號量的初始值。
  3. dsema_value即爲信號量當前值,信號量的相關API正是經過操做dsema_value來實現其功能的。
  4. _dispatch_sema4_t dsema_sema,信號量的結構。

_DISPATCH_OBJECT_HEADER是一個宏定義:數據結構

#define DISPATCH_OBJECT_HEADER(x) \ struct dispatch_object_s _as_do[0]; \ _DISPATCH_OBJECT_HEADER(x)
複製代碼

_DISPATCH_OBJECT_HEADER以下:多線程

#define _DISPATCH_OBJECT_HEADER(x) \ struct _os_object_s _as_os_obj[0]; \ OS_OBJECT_STRUCT_HEADER(dispatch_##x); \ struct dispatch_##x##_s *volatile do_next; \ struct dispatch_queue_s *do_targetq; \ void *do_ctxt; \ void *do_finalizer
複製代碼

其中有兩個成員比較關鍵:app

struct dispatch_##x##_s *volatile do_next; \
struct dispatch_queue_s *do_targetq; \
複製代碼

這兩個在後邊會講到。less

dispatch_object_s對象也使用了_DISPATCH_OBJECT_HEADER:函數

struct dispatch_object_s {
	_DISPATCH_OBJECT_HEADER(object);
};

/* * Dispatch objects are NOT C++ objects. Nevertheless, we can at least keep C++ * aware of type compatibility. */
typedef struct dispatch_object_s {
private:
	dispatch_object_s();
	~dispatch_object_s();
	dispatch_object_s(const dispatch_object_s &);
	void operator=(const dispatch_object_s &);
} *dispatch_object_t;

typedef union {
	struct _os_object_s *_os_obj;
	struct dispatch_object_s *_do;
	struct dispatch_queue_s *_dq;
	struct dispatch_queue_attr_s *_dqa;
	struct dispatch_group_s *_dg;
	struct dispatch_source_s *_ds;
	struct dispatch_mach_s *_dm;
	struct dispatch_mach_msg_s *_dmsg;
	struct dispatch_semaphore_s *_dsema;
	struct dispatch_data_s *_ddata;
	struct dispatch_io_s *_dchannel;
} dispatch_object_t DISPATCH_TRANSPARENT_UNION;
複製代碼

dispatch_object_t是一個聯合體,libdispatch中的全部對象都須要使用到。

dispatch_semaphore_create

/*! * @function dispatch_semaphore_create * * @abstract * Creates new counting semaphore with an initial value. * * @discussion * Passing zero for the value is useful for when two threads need to reconcile * the completion of a particular event. Passing a value greater than zero is * useful for managing a finite pool of resources, where the pool size is equal * to the value. * * @param value * The starting value for the semaphore. Passing a value less than zero will * cause NULL to be returned. * * @result * The newly created semaphore, or NULL on failure. */
API_AVAILABLE(macos(10.6), ios(4.0))
DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_RETURNS_RETAINED DISPATCH_WARN_RESULT DISPATCH_NOTHROW dispatch_semaphore_t dispatch_semaphore_create(long value);
複製代碼

參數value若是小於0,則無效。其實現源碼以下:

dispatch_semaphore_t
dispatch_semaphore_create(long value)
{
	dispatch_semaphore_t dsema;

	// If the internal value is negative, then the absolute of the value is
	// equal to the number of waiting threads. Therefore it is bogus to
	// initialize the semaphore with a negative value.
	if (value < 0) {
		return DISPATCH_BAD_INPUT;
	}

	dsema = _dispatch_object_alloc(DISPATCH_VTABLE(semaphore),
			sizeof(struct dispatch_semaphore_s));
	dsema->do_next = DISPATCH_OBJECT_LISTLESS;
	// 目標隊列
	dsema->do_targetq = _dispatch_get_default_queue(false);
    // 當前值
	dsema->dsema_value = value;
	_dispatch_sema4_init(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
    // 初始值
	dsema->dsema_orig = value;
	return dsema;
}
複製代碼

_dispatch_object_alloc

_dispatch_object_alloc的第一個參數DISPATCH_VTABLE(semaphore),設置了dispatch_semaphore_t的相關回調函數,如銷燬函數_dispatch_semaphore_dispose。

void *
_dispatch_object_alloc(const void *vtable, size_t size)
{
#if OS_OBJECT_HAVE_OBJC1
	const struct dispatch_object_vtable_s *_vtable = vtable;
	dispatch_object_t dou;
	dou._os_obj = _os_object_alloc_realized(_vtable->_os_obj_objc_isa, size);
	dou._do->do_vtable = vtable;
	return dou._do;
#else
	return _os_object_alloc_realized(vtable, size);
#endif
}
複製代碼

_os_object_alloc_realized函數以下,其中會調用_os_objc_alloc函數。

_os_object_t
_os_object_alloc_realized(const void *cls, size_t size)
{
	dispatch_assert(size >= sizeof(struct _os_object_s));
	return _os_objc_alloc(cls, size);
}
複製代碼
static inline id
_os_objc_alloc(Class cls, size_t size)
{
	id obj;
	size -= sizeof(((struct _os_object_s *)NULL)->os_obj_isa);
	while (unlikely(!(obj = class_createInstance(cls, size)))) {
		_dispatch_temporary_resource_shortage();
	}
	return obj;
}
複製代碼

DISPATCH_VTABLE(semaphore)

DISPATCH_VTABLE的定義以下:

#define DISPATCH_VTABLE(name) DISPATCH_OBJC_CLASS(name)

// vtable symbols
#define OS_OBJECT_VTABLE(name) (&OS_OBJECT_CLASS_SYMBOL(name))
#define DISPATCH_OBJC_CLASS(name) (&DISPATCH_CLASS_SYMBOL(name))

#define DISPATCH_CLASS_SYMBOL(name) OS_dispatch_##name##_class
複製代碼

實際上, DISPATCH_VTABLE(semaphore) 即爲 &OS_dispatch_semaphore_class

另外還有一個宏DISPATCH_VTABLE_INSTANCE,

DISPATCH_VTABLE_INSTANCE(semaphore,
	.do_type        = DISPATCH_SEMAPHORE_TYPE,
	.do_dispose     = _dispatch_semaphore_dispose,
	.do_debug       = _dispatch_semaphore_debug,
	.do_invoke      = _dispatch_object_no_invoke,
);

#define DISPATCH_VTABLE_INSTANCE(name, ...) \ DISPATCH_VTABLE_SUBCLASS_INSTANCE(name, name, __VA_ARGS__)

#define DISPATCH_VTABLE_SUBCLASS_INSTANCE(name, ctype, ...) \ OS_OBJECT_VTABLE_SUBCLASS_INSTANCE(dispatch_##name, dispatch_##ctype, \ _dispatch_xref_dispose, _dispatch_dispose, __VA_ARGS__) 

// vtables for proper classes
#define OS_OBJECT_VTABLE_INSTANCE(name, xdispose, dispose, ...) \ OS_OBJECT_VTABLE_SUBCLASS_INSTANCE(name, name, \ xdispose, dispose, __VA_ARGS__)

#define OS_OBJECT_VTABLE_SUBCLASS_INSTANCE(name, ctype, xdispose, dispose, ...) \ __attribute__((section("__DATA,__objc_data"), used)) \ const struct ctype##_extra_vtable_s \ OS_OBJECT_EXTRA_VTABLE_SYMBOL(name) = { __VA_ARGS__ }

#define OS_OBJECT_EXTRA_VTABLE_SYMBOL(name) _OS_##name##_vtable 
複製代碼

這一堆宏定義,繞來繞去,意圖就是創建一個vtable。vtable是虛函數表,能夠經過索引方式來快速獲取方法。相比於OC的方法查找,vtable的方式會有極大的性能提高。Swift中就大量使用了相似的vtable機制,如。

// 查處方法
let method = MyClass.vtable[methodIndex]
// 調用方法
method()
複製代碼

_dispatch_sema4_create

DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_sema4_create(_dispatch_sema4_t *sema, int policy)
{
	if (!_dispatch_sema4_is_created(sema)) {
		_dispatch_sema4_create_slow(sema, policy);
	}
}
複製代碼

_dispatch_sema4_create_slow的源碼以下:

#define _dispatch_sema4_is_created(sema) (*(sema) != MACH_PORT_NULL)

void
_dispatch_sema4_create_slow(_dispatch_sema4_t *s4, int policy)
{
	semaphore_t tmp = MACH_PORT_NULL;

	_dispatch_fork_becomes_unsafe();

	// lazily allocate the semaphore port

	// Someday:
	// 1) Switch to a doubly-linked FIFO in user-space.
	// 2) User-space timers for the timeout.

#if DISPATCH_USE_OS_SEMAPHORE_CACHE
	if (policy == _DSEMA4_POLICY_FIFO) {
		tmp = (_dispatch_sema4_t)os_get_cached_semaphore();
		if (!os_atomic_cmpxchg(s4, MACH_PORT_NULL, tmp, relaxed)) {
			os_put_cached_semaphore((os_semaphore_t)tmp);
		}
		return;
	}
#endif

	kern_return_t kr = semaphore_create(mach_task_self(), &tmp, policy, 0);
	DISPATCH_SEMAPHORE_VERIFY_KR(kr);

	if (!os_atomic_cmpxchg(s4, MACH_PORT_NULL, tmp, relaxed)) {
		kr = semaphore_destroy(mach_task_self(), tmp);
		DISPATCH_SEMAPHORE_VERIFY_KR(kr);
	}
}
複製代碼

若是用到了信號量緩存(DISPATCH_USE_OS_SEMAPHORE_CACHE),且是FIFO,則會直接從cache中取出信號量來使用。

不然,就使用semaphore_create來建立一個新的信號量。

dispatch_semaphore_wait

wait操做,會將信號量的值減一,若減操做後的結果值爲負數,則函數會一直等待信號量的釋放。

/*! * @function dispatch_semaphore_wait * * @abstract * Wait (decrement) for a semaphore. * * @discussion * Decrement the counting semaphore. If the resulting value is less than zero, * this function waits for a signal to occur before returning. * * @param dsema * The semaphore. The result of passing NULL in this parameter is undefined. * * @param timeout * When to timeout (see dispatch_time). As a convenience, there are the * DISPATCH_TIME_NOW and DISPATCH_TIME_FOREVER constants. * * @result * Returns zero on success, or non-zero if the timeout occurred. */
API_AVAILABLE(macos(10.6), ios(4.0))
DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
複製代碼

源碼實現以下:

long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout) {
	long value = os_atomic_dec2o(dsema, dsema_value, acquire);
	if (likely(value >= 0)) {
		return 0;
	}
	return _dispatch_semaphore_wait_slow(dsema, timeout);
}
複製代碼

dispatch_semaphore_wait一開始調用了系統的原子操做os_atomic_dec2o,將信號量的值減一。該操做以後,若信號量不是負數,則依然有信號量資源可用。若爲負數,則執行_dispatch_semaphore_wait_slow進入等待。

DISPATCH_NOINLINE
static long
_dispatch_semaphore_wait_slow(dispatch_semaphore_t dsema,
		dispatch_time_t timeout)
{
	long orig;

	_dispatch_sema4_create(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
	switch (timeout) {
	default:
		if (!_dispatch_sema4_timedwait(&dsema->dsema_sema, timeout)) {
			break;
		}
		// Fall through and try to undo what the fast path did to
		// dsema->dsema_value
	case DISPATCH_TIME_NOW:
		orig = dsema->dsema_value;
		while (orig < 0) {
			if (os_atomic_cmpxchgvw2o(dsema, dsema_value, orig, orig + 1,
					&orig, relaxed)) {
				return _DSEMA4_TIMEOUT();
			}
		}
		// Another thread called semaphore_signal().
		// Fall through and drain the wakeup.
	case DISPATCH_TIME_FOREVER:
		_dispatch_sema4_wait(&dsema->dsema_sema);
		break;
	}
	return 0;
}
複製代碼

_dispatch_semaphore_wait_slow函數根據timeout來決定等待行爲,

  1. 若是是一個特定的timeout時間,則調用_dispatch_sema4_timedwait(&dsema->dsema_sema, timeout)進行timeout時間的等待行爲。
  2. 若是是DISPATCH_TIME_NOW,先取得信號量的當前值,且該值必定爲非負數。由於若爲負數,則必定是上一次調用dispatch_semaphore_wait的時候執行的減操做變成負數,然而變成負數後,dispatch_semaphore_wait函數則會阻塞。以後,調用os_atomic_cmpxchgvw2o函數,會將信號量值加一,而後給到信號量的dsema_value變量。這個加一即爲抵消最初調用dispatch_semaphore_wait時的減一操做。因此DISPATCH_TIME_NOW這種狀況會當即返回超時。
  3. 若是是DISPATCH_TIME_FOREVER,則直接調用_dispatch_sema4_wait(&dsema->dsema_sema);永遠等待,直至信號量釋放。

_dispatch_sema4_wait和_dispatch_sema4_timedwait的源碼以下,

void
_dispatch_sema4_wait(_dispatch_sema4_t *sema)
{
	kern_return_t kr;
	do {
		kr = semaphore_wait(*sema);
	} while (kr == KERN_ABORTED);
	DISPATCH_SEMAPHORE_VERIFY_KR(kr);
}

bool
_dispatch_sema4_timedwait(_dispatch_sema4_t *sema, dispatch_time_t timeout)
{
	mach_timespec_t _timeout;
	kern_return_t kr;

	do {
		uint64_t nsec = _dispatch_timeout(timeout);
		_timeout.tv_sec = (typeof(_timeout.tv_sec))(nsec / NSEC_PER_SEC);
		_timeout.tv_nsec = (typeof(_timeout.tv_nsec))(nsec % NSEC_PER_SEC);
		kr = semaphore_timedwait(*sema, _timeout);
	} while (unlikely(kr == KERN_ABORTED));

	if (kr == KERN_OPERATION_TIMED_OUT) {
		return true;
	}
	DISPATCH_SEMAPHORE_VERIFY_KR(kr);
	return false;
}
複製代碼

其中調用了mach內核的信號量接口semaphore_wait和semaphore_timedwait進行wait操做。因此,GCD的信號量其實是基於mach內核的信號量接口來實現。semaphore_timedwait函數便可以指定超時時間。

dispatch_semaphore_signal

dispatch_semaphore_signal負責釋放信號量。

/*! * @function dispatch_semaphore_signal * * @abstract * Signal (increment) a semaphore. * * @discussion * Increment the counting semaphore. If the previous value was less than zero, * this function wakes a waiting thread before returning. * * @param dsema The counting semaphore. * The result of passing NULL in this parameter is undefined. * * @result * This function returns non-zero if a thread is woken. Otherwise, zero is * returned. */
API_AVAILABLE(macos(10.6), ios(4.0))
DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW long dispatch_semaphore_signal(dispatch_semaphore_t dsema);
複製代碼

dispatch_semaphore_signal的源碼以下:

long dispatch_semaphore_signal(dispatch_semaphore_t dsema) {
	long value = os_atomic_inc2o(dsema, dsema_value, release);
	if (likely(value > 0)) {
		return 0;
	}
	if (unlikely(value == LONG_MIN)) {
		DISPATCH_CLIENT_CRASH(value,
				"Unbalanced call to dispatch_semaphore_signal()");
	}
	return _dispatch_semaphore_signal_slow(dsema);
}
複製代碼

dispatch_semaphore_signal操做,則是執行了原子操做os_atomic_inc2o,將信號量的值加一。若過分釋放,致使信號量的值爲LONG_MIN,則會觸發crash,信息爲 ***Unbalanced call to dispatch_semaphore_signal()***。因此,跟GCD group的enter/leave相似,過分調用dispatch_semaphore_signal,理論上來講會致使崩潰。但並未實際復現出來,很奇怪。

而_dispatch_semaphore_signal_slow實際上會調用mach內核的semaphore_signal函數。

DISPATCH_NOINLINE
long
_dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema)
{
	_dispatch_sema4_create(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
	_dispatch_sema4_signal(&dsema->dsema_sema, 1);
	return 1;
}

void
_dispatch_sema4_signal(_dispatch_sema4_t *sema, long count)
{
	do {
		kern_return_t kr = semaphore_signal(*sema);
		DISPATCH_SEMAPHORE_VERIFY_KR(kr);
	} while (--count);
}
複製代碼

semaphore_signal可以喚醒一個在semaphore_wait中等待的線程。若是有多個等待線程,則根據線程優先級來喚醒。

_dispatch_semaphore_dispose

信號量的銷燬函數以下:

void
_dispatch_semaphore_dispose(dispatch_object_t dou,
		DISPATCH_UNUSED bool *allow_free)
{
	dispatch_semaphore_t dsema = dou._dsema;

	if (dsema->dsema_value < dsema->dsema_orig) {
		DISPATCH_CLIENT_CRASH(dsema->dsema_orig - dsema->dsema_value,
				"Semaphore object deallocated while in use");
	}

	_dispatch_sema4_dispose(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
}
複製代碼

這裏有一個判斷,若 dsema->dsema_value < dsema->dsema_orig,則致使崩潰,並提示 Semaphore object deallocated while in use。這也是容易遇到的問題之一,後邊會講到。

_dispatch_sema4_dispose代碼以下:

DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_sema4_dispose(_dispatch_sema4_t *sema, int policy)
{
	if (_dispatch_sema4_is_created(sema)) {
		_dispatch_sema4_dispose_slow(sema, policy);
	}
}
複製代碼

進一步往下,會調用_dispatch_sema4_dispose_slow函數,

void
_dispatch_sema4_dispose_slow(_dispatch_sema4_t *sema, int policy)
{
	semaphore_t sema_port = *sema;
	*sema = MACH_PORT_DEAD;
#if DISPATCH_USE_OS_SEMAPHORE_CACHE
	if (policy == _DSEMA4_POLICY_FIFO) {
		return os_put_cached_semaphore((os_semaphore_t)sema_port);
	}
#endif
	kern_return_t kr = semaphore_destroy(mach_task_self(), sema_port);
	DISPATCH_SEMAPHORE_VERIFY_KR(kr);
}
複製代碼

若是使用了信號量緩存,且FIFO,則將待回收的信號量對象放入緩存便可。不然,調用mach內核的semaphore_destroy函數進行信號量的銷燬。

一些崩潰

跟GCD group的enter/leave相似,這一類接口要保證操做的平衡。不然可能致使嚴重的問題。蘋果的文檔已經說得很清楚了:

Calls to dispatch_semaphore_signal must be balanced with calls to dispatch_semaphore_wait. Attempting to dispose of a semaphore with a count lower than value causes an EXC_BAD_INSTRUCTION exception.

Unbalanced call to dispatch_semaphore_signal()

這一點暫時未能復現。不過看原理,應該就是過分調用dispatch_semaphore_signal致使的。

Semaphore object deallocated while in use

Exception Type:  EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000001, 0x00000001a3b77ff4
Termination Signal: Trace/BPT trap: 5
Termination Reason: Namespace SIGNAL, Code 0x5
Terminating Process: exc handler [249]
Triggered by Thread:  15

Application Specific Information:
BUG IN CLIENT OF LIBDISPATCH: Semaphore object deallocated while in use
Abort Cause 1
Thread 15 name:  Dispatch queue: com.xxxx.xxxx.xxxxQueue (QOS: UNSPECIFIED)

Thread 15 Crashed:
0   libdispatch.dylib             	0x00000001a3b77ff4 0x1a3b75000 + 12276
1   libdispatch.dylib             	0x00000001a3b77014 0x1a3b75000 + 8212
2   libobjc.A.dylib               	0x00000001a336e7cc 0x1a336a000 + 18380
3   libobjc.A.dylib               	0x00000001a337e6b8 0x1a336a000 + 83640
4   libobjc.A.dylib               	0x00000001a337e720 0x1a336a000 + 83744
5   XXXX                          	0x000000010535056c -[MyXXXXObject dealloc] + 70960492 (MyXXXXObject.mm:xx)
6   XXXX                          	0x000000010535171c __destroy_helper_block_ea8_32s40s48s56s64r + 70965020 (MyXXXXObject.mm:xxx)
7   libsystem_blocks.dylib        	0x00000001a3c30a44 0x1a3c30000 + 2628
8   Foundation                    	0x00000001a4b09410 0x1a4aea000 + 128016
9   Foundation                    	0x00000001a4b97330 0x1a4aea000 + 709424
10  libsystem_blocks.dylib        	0x00000001a3c30a44 0x1a3c30000 + 2628
11  libdispatch.dylib             	0x00000001a3bd57d4 0x1a3b75000 + 395220
12  libdispatch.dylib             	0x00000001a3b7a01c 0x1a3b75000 + 20508
13  libdispatch.dylib             	0x00000001a3b796e0 0x1a3b75000 + 18144
14  libdispatch.dylib             	0x00000001a3b86030 0x1a3b75000 + 69680
15  libdispatch.dylib             	0x00000001a3b868d4 0x1a3b75000 + 71892
16  libsystem_pthread.dylib       	0x00000001a3db61b4 0x1a3daa000 + 49588
17  libsystem_pthread.dylib       	0x00000001a3db8cd4 0x1a3daa000 + 60628
複製代碼

這類crash的緣由在於對象釋放的時候,其內部持有的信號量對象依然在使用(即dsema->dsema_value < dsema->dsema_orig,信號量的值未經過signal操做恢復到其原始值)。常見於:其餘線程執行了wait操做,而在沒有對應signal的前提下,即將該信號量釋放了(持有信號量的對象釋放致使的)。

能夠經過下邊代碼來複現,以查看調用堆棧。

dispatch_semaphore_t sema = dispatch_semaphore_create(1);
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);

sema = dispatch_semaphore_create(1);
複製代碼

在信號量依然在使用的時候,從新給sema賦值就會致使崩潰,即使是 sema = nil; 也會。堆棧以下:

_dispatch_semaphore_dispose.cold.1
_disaptch_semaphore_dispose
_dispatch_dispose
sema = dispatch_semaphore_create(1);
複製代碼

固然,_disaptch_semaphore_dispose中會調用_dispatch_sema4_dispose_slow,這些咱們在以前代碼就已經分析過了。

解決辦法:若是對於已知場景的信號量,好比信號量初始值爲1的狀況,能夠在對象dealloc的時候手動執行一次signal操做,以避免出現 dsema->dsema_value < dsema->dsema_orig 的狀況。畢竟多執行了signal操做,目前是不會有問題的。

然而,實際的使用場景仍是要儘可能保證信號量的wait和signal保持平衡,這樣代碼邏輯纔不會有問題。

參考資料

相關文章
相關標籤/搜索