互斥锁在解锁之前是否会阻止访问?

Does a mutex lock prevent access until it's unlocked?

假设这个 C 代码:

#include <stdio.h> 
#include <stdlib.h> 
#include <pthread.h> 
#include <unistd.h>

pthread_mutex_t lock;  
int a = 0;

void *myThreadFun(void *vargp) 
{ 
   pthread_mutex_lock(&lock); 
   a = 5;
   while (1) sleep(1);
   pthread_mutex_unlock(&lock);
   return NULL; 
} 

int main() 
{
   pthread_mutex_init(&lock, NULL);
   pthread_t thread_id; 
   pthread_create(&thread_id, NULL, myThreadFun, NULL);
   while (1){
    a = 6;
    sleep(1); 
    printf("%d\n", a);
   }       
   pthread_join(thread_id, NULL); 
}

第一个输出是5,但随后的输出都是6。那个怎么样? myThreadFun 创建一个锁并进入无限循环并且永远不会解锁,那么 main 如何覆盖 a

放在lock之间的任何东西都会受到保护?也就是说,如果我在 myThreadFun 中有更多变量要保护,我只是将它们放在同一个锁之间?

锁是在解锁之前阻止访问,还是只在自己的 read/write 完成之前阻止访问?即防止部分读写?

myThreadFun creates a lock and goes in an infinite loop and never unlocks

是的,但是那把锁是用来做什么的?没有什么。除了线程,没有其他人在使用锁。 main 程序在不请求任何锁的情况下访问变量。该程序不知道锁应该保护 a 变量这一事实。它真的可以用于任何事情。锁只能防止代码的关键区域被多个线程同时执行,因为所有线程都在关键区域的开头锁定并在结尾解锁。

如果您希望它正确运行,则必须正确使用锁,并且 lock/release 它适用于与变量交互的代码的每个部分。像这样(在 main 中):

while (1) {
    pthread_mutex_lock(&lock);
    a = 6;
    sleep(1);
    printf("%d\n", a);
    pthread_mutex_unlock(&lock);
}

Does a lock block access until it's unlocked, or it block access only until its own read/write is done? i.e to prevent a partial read and write?

锁对read/write、代码或变量一无所知。锁只是一个具有两种状态的对象:锁定或解锁。如果它的状态是解锁的,那么它就可以被锁定。如果它的状态是锁定的,那么在解锁发生之前它不能被锁定(并且请求锁导致线程等待直到锁被解锁)。

This still prints 6. I want it to print 5. I want to lock a in a thread so no other thread can touch it.

这是一个不同的问题。您在这里需要做的是确保您启动的线程在主线程之前可以访问a变量。如果要实现此结果,则需要使两者同步。换句话说,您希望 main 程序等待直到线程持有变量的锁。

这可以通过不同的方式实现。这是一个使用信号量的工作示例(查看 man sem_overview 了解更多信息)。 注意 虽然使用另一个互斥量而不是信号量似乎可以实现以下内容,但事实并非如此。互斥量和信号量之间的主要区别在于,互斥量只能由锁定它的同一个线程解锁,而信号量可以由不同的线程自由地锁定或解锁。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>

sem_t sem_main;
pthread_mutex_t lock_var_a;
int a = 0;

void *myThreadFun(void *vargp)
{
    pthread_mutex_lock(&lock_var_a);

    // Signal main that I acquired the lock.
    sem_post(&sem_main);

    a = 5;
    while (1) {
        printf("Thread: a = %d\n", a);
        sleep(1);
    }

    pthread_mutex_unlock(&lock_var_a);

    return NULL;
}

int main()
{
    sem_init(&sem_main, 0, 0);
    pthread_mutex_init(&lock_var_a, NULL);

    pthread_t thread_id;
    pthread_create(&thread_id, NULL, myThreadFun, NULL);

    // Wait for thread to acquire the lock.
    sem_wait(&sem_main);

    while (1){
        pthread_mutex_lock(&lock_var_a);

        // This code will never be executed.
        a = 6;
        printf("Main: a = %d\n", a);
        sleep(1);

        pthread_mutex_unlock(&lock_var_a);
    }

    pthread_join(thread_id, NULL);
}

在上面的例子程序中main会等待线程获取锁之后再继续。结果输出将是:

Thread: a = 5
Thread: a = 5
Thread: a = 5
...

如果您希望某个变量只能访问特定线程,请在内部线程函数中声明它:

void *myThreadFun(void *unused) 
{ 
   int a = 5;
   while (1) sleep(1);
   return 0;
}

现在main根本搞不定,加锁也没用