互斥锁线程

39

我是多线程/进程编程的新手,这是我需要澄清的事情。

进程A的代码

pthread_mutex_lock()
    pthread_create(fooAPI(sharedResource)) //fooAPI creates another thread with shared resource that shares across processes.
pthread_mutex_unlock()

使用上述伪代码,如果互斥锁未解锁,进程B能否访问sharedResource

如何正确地从进程B访问共享资源?

是否有任何清晰的可视化图表说明互斥锁、线程和进程之间的关系?


也许我们应该将这个问题的标题改为“在 C 中使用pthread_mutex_lock()锁定线程”之类的内容,以使问题更易搜索并且具有更清晰的开头? - Gabriel Staples
我刚刚提出的相关问题:C语言中的互斥锁:C11 mtx_lock() vs pthread_mutex_lock() - Gabriel Staples
4个回答

66

您需要调用pthread_mutex_lock来保护互斥锁,如下所示:

pthread_mutex_lock(&mutex);
一旦你这样做了,在该线程中调用pthread_mutex_lock(mutex)的任何其他调用都不会返回,直到你在该线程中调用了pthread_mutex_unlock。因此,如果你尝试调用pthread_create,你将能够创建一个新线程,而该线程将能够(错误地)使用共享资源。你应该从你的fooAPI函数中调用pthread_mutex_lock,这将导致函数等待直到共享资源可用。

所以你会有类似这样的代码:

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

int sharedResource = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void* fooAPI(void* param)
{
    pthread_mutex_lock(&mutex);
    printf("Changing the shared resource now.\n");
    sharedResource = 42;
    pthread_mutex_unlock(&mutex);
    return 0;
}

int main()
{
    pthread_t thread;

    // Really not locking for any reason other than to make the point.
    pthread_mutex_lock(&mutex);
    pthread_create(&thread, NULL, fooAPI, NULL);
    sleep(1);
    pthread_mutex_unlock(&mutex);

    // Now we need to lock to use the shared resource.
    pthread_mutex_lock(&mutex);
    printf("%d\n", sharedResource);
    pthread_mutex_unlock(&mutex);
}

编辑:跨进程使用资源遵循相同的基本方法,但需要将内存映射到其他进程中。这里是使用shmem的示例:

#include <stdio.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <sys/wait.h>

struct shared {
    pthread_mutex_t mutex;
    int sharedResource;
};

int main()
{
    int fd = shm_open("/foo", O_CREAT | O_TRUNC | O_RDWR, 0600);
    ftruncate(fd, sizeof(struct shared));

    struct shared *p = (struct shared*)mmap(0, sizeof(struct shared),
        PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

    p->sharedResource = 0;

    // Make sure it can be shared across processes
    pthread_mutexattr_t shared;
    pthread_mutexattr_init(&shared);
    pthread_mutexattr_setpshared(&shared, PTHREAD_PROCESS_SHARED);

    pthread_mutex_init(&(p->mutex), &shared);

    int i;
    for (i = 0; i < 100; i++) {
        pthread_mutex_lock(&(p->mutex));
        printf("%d\n", p->sharedResource);
        pthread_mutex_unlock(&(p->mutex));
        sleep(1);
    }

    munmap(p, sizeof(struct shared*));
    shm_unlink("/foo");
}

编写程序更改p->sharedResource的过程留给读者自己完成。 :-)

顺便提醒一下,mutex必须具有PTHREAD_PROCESS_SHARED属性设置,以便pthread可以跨进程工作。


这非常清晰。您能否提供一个示例,说明另一个应用程序Process B如何访问sharedResource? - resting

4
Q1.) 假设进程B试图获取与进程A锁定的相同互斥量的所有权(您在伪代码中省略了这一点),那么不,进程B无法在互斥量被锁定时访问sharedResource,因为它将等待锁定互斥量,直到它被进程A释放。当互斥量被锁定(或发生错误)时,它将从mutex_lock()函数返回。
Q2.) 在进程B中,请确保始终锁定互斥量、访问共享资源,然后解锁互斥量。此外,请检查mutex_lock(pMutex)例程的返回代码,以确保您实际拥有互斥量,并且只有在锁定互斥量后才解锁互斥量。在进程A中也要做同样的事情。
当访问互斥量时,两个进程应该基本上做相同的事情。 lock() 如果锁定成功,则{ 访问共享资源 解锁() }
Q3.) 是的,有很多图表:=) https://www.google.se/search?q=mutex+thread+process&rlz=1C1AFAB_enSE487SE487&um=1&ie=UTF-8&hl=en&tbm=isch&source=og&sa=N&tab=wi&ei=ErodUcSmKqf54QS6nYDoAw&biw=1200&bih=1730&sei=FbodUbPbB6mF4ATarIBQ

3
一个进程至少包含一个线程(可以认为是主函数)。多线程代码将会产生更多的线程。互斥锁用于创建围绕共享资源的锁,以避免数据损坏/意外/不必要的行为。基本上,它提供了在异步设置中的顺序执行——这一要求源自对共享数据结构进行非常量非原子操作。
一个生动的描述互斥锁的例子是人们(线程)排队等候使用公共洗手间(共享资源)。当一个人(线程)正在使用浴室放松自己(非常量非原子操作)时,他/她应该确保门是锁着的(互斥锁),否则可能会被抓个正着(不必要的行为)。

3

以下是一段代码片段,可以帮助您理解互斥锁的概念。请尝试在代码上进行干运行(通过更改等待时间和处理时间,您可以进一步建立自己的理解)。

代码如下:

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

void in_progress_feedback(int);

int global = 0;
pthread_mutex_t mutex;
void *compute(void *arg) {

    pthread_t ptid = pthread_self();
    printf("ptid : %08x \n", (int)ptid);    

    int i;
    int lock_ret = 1;   
    do{

        lock_ret = pthread_mutex_trylock(&mutex);
        if(lock_ret){
            printf("lock failed(%08x :: %d)..attempt again after 2secs..\n", (int)ptid,  lock_ret);
            sleep(2);  //wait time here..
        }else{  //ret =0 is successful lock
            printf("lock success(%08x :: %d)..\n", (int)ptid, lock_ret);
            break;
        }

    } while(lock_ret);

        for (i = 0; i < 10*10 ; i++) 
        global++;

    //do some stuff here
    in_progress_feedback(10);  //processing-time here..

    lock_ret = pthread_mutex_unlock(&mutex);
    printf("unlocked(%08x :: %d)..!\n", (int)ptid, lock_ret);

     return NULL;
}

void in_progress_feedback(int prog_delay){

    int i=0;
    for(;i<prog_delay;i++){
    printf(". ");
    sleep(1);
    fflush(stdout);
    }

    printf("\n");
    fflush(stdout);
}

int main(void)
{
    pthread_t tid0,tid1;
    pthread_mutex_init(&mutex, NULL);
    pthread_create(&tid0, NULL, compute, NULL);
    pthread_create(&tid1, NULL, compute, NULL);
    pthread_join(tid0, NULL);
    pthread_join(tid1, NULL);
    printf("global = %d\n", global);
    pthread_mutex_destroy(&mutex);
          return 0;
}

函数void *compute()只有在被调用时才会执行吗?还是有一种方法可以启动此线程,使其在后台持续执行此函数? - LandonZeKepitelOfGreytBritn
当一个线程被创建时,与该线程绑定的方法开始在该线程上执行(在本例中为compute())。如果没有创建线程,则仍然在主线程上(其中main()执行)。现在,如果您需要使线程在后台执行某些操作,则需要在绑定方法上保持该执行状态,直到满足某个非退出条件(即不要让绑定方法退出,直到您要求关闭)。现在,当向线程发出退出信号时,它应该退出(清理等),否则它将继续执行您所需的操作。 - parasrish

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接