c - 线程互斥行为

标签 c mutex

我正在学习 C。我正在编写一个具有多线程的应用程序;我知道当一个变量在两个或多个线程之间共享时,最好使用互斥锁来锁定/解锁以避免变量的死锁和不一致。当我想更改或查看一个变量时,这一点非常清楚。

int i = 0; /** Global */
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

/** Thread 1. */
pthread_mutex_lock(&mutex);
i++;
pthread_mutex_unlock(&mutex);

/** Thread 2. */
pthread_mutex_lock(&mutex);
i++;
pthread_mutex_unlock(&mutex);

我认为这是正确的。执行结束时,变量 i 包含整数 2
不管怎样,在某些情况下我不知道将两个函数调用放在哪里。

例如,假设您有一个函数obtain(),它返回一个全局变量。我需要从两个线程中调用该函数。我还有另外两个调用函数 set() 的线程,定义了几个参数;此函数将设置相同的全局变量。当您需要在获取/设置 var 之前执行某些操作时,这两个函数是必需的。

/** (0) */
/** Thread 1, or 2, or 3... */
if(obtain() == something) {

    if(obtain() == somethingElse) {
        // Do this, sometimes obtain() and sometimes set(random number) (1)   
    } else {
        // Do that, just obtain(). (2)
    }

} else {
    // Do this and do that (3)
    // If # of thread * 3 > 10, then set(3*10) For example. (4)
}
/** (5) */

我必须在哪里锁定,在哪里必须解锁? 我认为,情况可能更加复杂。我将不胜感激一个详尽的答案。

提前谢谢你。
—阿尔贝托

最佳答案

没有任何保护:

操作系统可能会随时中断您的每个线程,并将处理器交给另一个。 “随时”包括“实际上来自同一个 C 命令的两个汇编指令之间”。

现在,假设您的变量在 32 位处理器中占用 64 位。这意味着您的变量占用两个处理器“字”。为了编写它,处理器需要两条汇编指令。读书也一样。如果线程在两者之间中断,就会有麻烦。

为了给出一个更清楚的例子,我将用两个十进制数字的类比来表示两个二进制32位字。假设您要在 1 位处理器中递增两位十进制数。要将 19 递增到 20,您必须读取 19,进行数学运算,然后写入 20。为了写入 20,您必须写入 2,然后写入 0(反之亦然)。如果你写 2,然后在写 0 之前被打断,内存中的数字将是 29,与实际正确的数字相去甚远。然后另一个线程继续读取错误的数字。

Blank Xavier 解释说,即使您只有一个数字,仍然存在读-修改-写问题。

使用互斥锁:

当线程 A 锁定互斥体时,线程 A 检查一个互斥体变量。如果它是免费的,线程 A 将其写入已占用的状态。它使用原子指令,一个汇编指令来完成它,因此没有“中间”中断。然后继续从 19 增加到 20。它仍然可以在不正确的 29 变量值期间被中断,但是没关系,因为现在没有其他人可以访问该变量。当线程 B 试图锁定互斥量时,它检查互斥量变量,它被获取。所以线程 B 知道它不能接触变量。然后它调用操作系统,说“我暂时放弃处理器”。如果线程 B 再次获得处理器,它将重复该操作。然后再次。直到线程 A 最终取回处理器,完成它正在做的事情,然后解锁互斥体。

那么,什么时候锁定?

至于很多事情,这取决于。主要是根据您的应用程序正常工作所需的特定行为顺序。您需要始终在读取写入之前锁定以获得保护,然后再解锁。但是“锁定的代码块”可能有很多命令,也可能只有一个。牢记上面解释的舞蹈,并考虑您的应用程序应该如何表现。

还有性能问题。如果你围绕每一行代码进行锁定/解锁,你就会浪费时间进行锁定/解锁。如果您只在大块代码周围锁定/解锁,那么每个线程都会等待很长时间才能释放互斥量。

并不是真的“总是”

现在,有些情况下您可以跳过锁定-解锁。当您处理一个一位数(意味着一个处理器字)变量时,它们会发生,并且每个线程要么只读它,要么只写它,所以读取的值不会决定以后写入什么值。仅当您非常确定自己在做什么并且确实需要提高性能时才这样做。

关于c - 线程互斥行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4718727/

相关文章:

c - 为什么 printf() 将 double 变量打印为 f 字符?

c++ - 返回指向函数中指针的指针

c - 代码说明

c++ - 我可以在 C++ 中使用互斥锁或关键字(静态)而不是 volatile 吗?

c++ - 使用 ARC 在 C 函数中取消引用 self,引用作为 intptr_t 传递

c++ - 何时使用 C++ 类锁定互斥体

c# - 单个应用程序实例的 WPF 互斥锁不起作用

c++ - TBB spin_mutex inside parallel_for 阻塞临界区

c++ - C/C++ - sem_t 类型的单个信号量按顺序打印数字

c代码文件崩溃并且编译时没有错误