gpt4 book ai didi

c++ - 为什么mutex可以在不同的线程中使用?

转载 作者:行者123 更新时间:2023-12-05 03:19:59 26 4
gpt4 key购买 nike

  1. 在多个线程中同时使用(写入)同一个变量会导致未定义的行为和崩溃。为什么使用互斥体,尽管事实上它们也是变量,却不会导致未定义的行为?

  2. 如果可以同时使用互斥量,为什么不让所有变量在不锁定的情况下同时工作?

我所有的研究都是在 Visual Studio 中的 mutex::lock 上按 Show definition,我在 _Mtx_lock 函数的最后没有实现, 然后我发现它是 realization (Windows) ,虽然它有一些功能也没有实现:

 int _Mtx_lock(_Mtx_t mtx)
{ /* lock mutex */
return (mtx_do_lock(mtx, 0));
}

static int mtx_do_lock(_Mtx_t mtx, const xtime *target)
{ /* lock mutex */
if ((mtx->type & ~_Mtx_recursive) == _Mtx_plain)
{ /* set the lock */
if (mtx->thread_id != static_cast<long>(GetCurrentThreadId()))
{ /* not current thread, do lock */
mtx->_get_cs()->lock();
mtx->thread_id = static_cast<long>(GetCurrentThreadId());
}
++mtx->count;

return (_Thrd_success);
}
else
{ /* handle timed or recursive mutex */
int res = WAIT_TIMEOUT;
if (target == 0)
{ /* no target --> plain wait (i.e. infinite timeout) */
if (mtx->thread_id != static_cast<long>(GetCurrentThreadId()))
mtx->_get_cs()->lock();
res = WAIT_OBJECT_0;

}
else if (target->sec < 0 || target->sec == 0 && target->nsec <= 0)
{ /* target time <= 0 --> plain trylock or timed wait for */
/* time that has passed; try to lock with 0 timeout */
if (mtx->thread_id != static_cast<long>(GetCurrentThreadId()))
{ /* not this thread, lock it */
if (mtx->_get_cs()->try_lock())
res = WAIT_OBJECT_0;
else
res = WAIT_TIMEOUT;
}
else
res = WAIT_OBJECT_0;

}
else
{ /* check timeout */
xtime now;
xtime_get(&now, TIME_UTC);
while (now.sec < target->sec
|| now.sec == target->sec && now.nsec < target->nsec)
{ /* time has not expired */
if (mtx->thread_id == static_cast<long>(GetCurrentThreadId())
|| mtx->_get_cs()->try_lock_for(
_Xtime_diff_to_millis2(target, &now)))
{ /* stop waiting */
res = WAIT_OBJECT_0;
break;
}
else
res = WAIT_TIMEOUT;

xtime_get(&now, TIME_UTC);
}
}
if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED)
;

else if (1 < ++mtx->count)
{ /* check count */
if ((mtx->type & _Mtx_recursive) != _Mtx_recursive)
{ /* not recursive, fixup count */
--mtx->count;
res = WAIT_TIMEOUT;
}
}
else
mtx->thread_id = static_cast<long>(GetCurrentThreadId());

switch (res)
{
case WAIT_OBJECT_0:
case WAIT_ABANDONED:
return (_Thrd_success);

case WAIT_TIMEOUT:
if (target == 0 || (target->sec == 0 && target->nsec == 0))
return (_Thrd_busy);
else
return (_Thrd_timedout);

default:
return (_Thrd_error);
}
}
}

所以,根据这段代码,加上atomic_关键字我觉得mutex可以这样写:

atomic_bool state = false;

void lock()
{
if(!state)
state = true;
else
while(state){}
}

void unlock()
{
state = false;
}

bool try_lock()
{
if(!state)
state = true;
else
return false;

return true;
}

最佳答案

如您所见,std::mutex 是线程安全的,因为它使用原子操作。它可以用 std::atomic_bool 重现。从多个线程使用原子变量不是未定义的行为,因为这是这些变量的目的。

来自 C++ 标准(强调我的):

The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior.

原子变量是使用 atomic operations of the CPU 实现的.这不是针对非原子变量实现的,因为这些操作需要更长的时间来执行,并且如果变量仅在一个线程中使用则将毫无用处。

你的例子不是线程安全的:

void lock()
{
if(!state)
state = true;
else
while(state){}
}

如果两个线程同时检查if(!state),则有可能两个线程都进入了if部分,并且两个线程都认为它们拥有所有权:

Thread 1        Thread 2
if (!state)
if (!state)
state=true;
state=true;

您必须使用 an atomic exchange function以确保另一个线程不能在检查值和更改值之间介入。

void lock()
{
bool expected;
do {
expected = false;
} while (!state.compare_exchange_weak(expected, true));
}

如果等待时间长,也可以加一个计数器,给其他线程执行的时间:

void lock()
{
bool expected;
size_t counter = 0;
do {
expected = false;
if (counter > 100) {
Sleep(10);
}
else if (counter > 20) {
Sleep(5);
}
else if (counter > 3) {
Sleep(1);
}
counter++;
} while (!state.compare_exchange_weak(expected, true));
}

关于c++ - 为什么mutex可以在不同的线程中使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73303037/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com