线程从未获得锁(pthread\u mutex\u lock) 故事

线程从未获得锁(pthread\u mutex\u lock) 故事,c,multithreading,pthreads,locking,mutex,C,Multithreading,Pthreads,Locking,Mutex,根据手册页 互斥体引用的互斥体对象应通过调用pthread_mutex_lock进行锁定。如果互斥锁已经锁定,调用线程将阻塞,直到互斥锁可用 我有一个带线程的程序。以下是程序流程: 主进程和线程总是在循环内调用pthread_mutex_lock。 当主进程持有锁时,请求锁的线程阻塞等待授予锁。 当主进程使用pthread_mutex_unlock释放锁时,线程应该会突然获得锁。 当主进程再次请求锁时,主进程应该等待线程释放锁。 问题是,在第3点,当主进程释放锁时,线程不会立即获得锁。主进程在第

根据手册页

互斥体引用的互斥体对象应通过调用pthread_mutex_lock进行锁定。如果互斥锁已经锁定,调用线程将阻塞,直到互斥锁可用

我有一个带线程的程序。以下是程序流程:

主进程和线程总是在循环内调用pthread_mutex_lock。 当主进程持有锁时,请求锁的线程阻塞等待授予锁。 当主进程使用pthread_mutex_unlock释放锁时,线程应该会突然获得锁。 当主进程再次请求锁时,主进程应该等待线程释放锁。 问题是,在第3点,当主进程释放锁时,线程不会立即获得锁。主进程在第4点的下一个循环周期中调用pthread_mutex_lock时首先获得它

如何处理这种情况

问题 如何使线程在主进程释放锁后立即获得锁

重现问题的简单代码 包括 包括 包括 pthread\u mutex\u t my\u mutex=pthread\u mutex\u初始值设定项; 空虚* 我的_-threadvoid*p { voidp; 而1{ pthread_mutex_lock&my_mutex; printf线程持有锁…\n; 睡眠1; pthread_mutex_unlock&my_mutex; } } int 主要的 { pthread_t; pthread_create&t,NULL,my_线程,NULL; pthread_detacht; 而1{ pthread_mutex_lock&my_mutex; printf主进程正在锁定…\n; 睡眠1; pthread_mutex_unlock&my_mutex; } } 编译并运行 预期结果 实际结果 把故事安排得井井有条 总结 pthread\u mutex\u lock不保证锁请求的顺序。

pthread\u mutex\u lock保证它将锁定,直到互斥变为可用。这并不意味着每个锁调用都会进入一个队列,并保证接下来会获得互斥锁。这只意味着没有其他人会同时拥有锁

如果您需要特定的顺序,可以选择使用条件变量。这样,您就可以将一个标志设置为下一个应该获得互斥锁的成员。然后,您可以等待互斥锁,直到值达到预期值。看

或者,如果您的示例中有睡眠,如上所述,您可以在解锁调用后移动睡眠。虽然严格来说这不是一种保证,但对于一个简单的测试来说,它肯定会起作用。不过,我不建议对更严重/复杂的问题采用这种方法

编辑:正如Shawn正确添加的,如果您不关心其他线程是哪个线程,您还可以使用pthread_yield来允许其他线程获取互斥体。中描述了屈服的一些复杂情况

PS:我想发表评论,但我的代表现在已经足够高了:

pthread\u mutex\u lock保证它将锁定,直到mutex可用为止。这并不意味着每个锁调用都会进入一个队列,并保证接下来会获得互斥锁。这只意味着没有其他人会同时拥有锁

如果您需要特定的顺序,可以选择使用条件变量。这样,您就可以将一个标志设置为下一个应该获得互斥锁的成员。然后,您可以等待互斥锁,直到值达到预期值。看

或者,如果您的示例中有睡眠,如上所述,您可以在解锁调用后移动睡眠。虽然严格来说这不是一种保证,但对于一个简单的测试来说,它肯定会起作用。不过,我不建议对更严重/复杂的问题采用这种方法

编辑:正如Shawn正确添加的,如果您不关心其他线程是哪个线程,您还可以使用pthread_yield来允许其他线程获取互斥体。中描述了屈服的一些复杂情况


PS:我会评论,但我的代表现在已经足够高了:

这里有一个“fairlock”作为例子;你可以做得更好:

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

typedef struct fairlock FairLock;
struct fairlock {
    pthread_mutex_t lock;
    pthread_cond_t  cv;
    long scount;
    long wcount;
};



#define err(x,v) do { int t; if ((t=(x)) != (v)) { \
    error(__FILE__,__LINE__, #x, t, (v)); \
}} while (0)

static void error(char *fn, int lno, char *s, long x, long v) {
    fprintf(stderr, "%s:%d %s returned %ld rather than %ld\n",
        fn, lno, s, x, v);
    exit(1);
}


void Lock(FairLock *f) {

    err(pthread_mutex_lock(&f->lock), 0);
    long me = f->scount++;
    while (f->wcount != me) {
        err(pthread_cond_wait(&f->cv, &f->lock), 0);
    }
    err(pthread_mutex_unlock(&f->lock), 0);

}

void UnLock(FairLock *f) {
    err(pthread_mutex_lock(&f->lock), 0);
    if (f->scount > f->wcount) {
        f->wcount++;
        err(pthread_cond_broadcast(&f->cv), 0);
    }
    err(pthread_mutex_unlock(&f->lock), 0);
}

FairLock *NewLock(void) {
    FairLock *p = malloc(sizeof *p);
    if (p != 0) {
        err(pthread_mutex_init(&p->lock, 0),0);
        err(pthread_cond_init(&p->cv, 0),0);
        p->scount = p->wcount = 0;
    }
    return p;
}

void DoneLock(FairLock *f) {
    err(pthread_mutex_destroy(&f->lock), 0);
    err(pthread_cond_destroy(&f->cv), 0);
}
并且您的testlock.c更改为使用它;同样还有改进的余地,但你应该能够坚持在任何地方睡觉,这将是公平的

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

#include "fairlock.c"

FairLock *my;

void *
my_thread(void *p)
{
  while (1) {
    Lock(my);
    printf("%s is holding the lock...\n", p);
    UnLock(my);
  }
}



int
main()
{
  pthread_t t;
  my = NewLock();
  pthread_create(&t, NULL, my_thread, "one");
  pthread_detach(t);
  pthread_create(&t, NULL, my_thread, "two");
  pthread_detach(t);
  pthread_create(&t, NULL, my_thread, "three");
  pthread_detach(t);
  pthread_create(&t, NULL, my_thread, "four");
  pthread_detach(t);
  pthread_create(&t, NULL, my_thread, "five");
  pthread_detach(t);

  while (1) {
    Lock(my);
    printf("main process is holding the lock...\n");
    UnLock(my);
  }
}

这里以“fairlock”为例;你可以做得更好:

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

typedef struct fairlock FairLock;
struct fairlock {
    pthread_mutex_t lock;
    pthread_cond_t  cv;
    long scount;
    long wcount;
};



#define err(x,v) do { int t; if ((t=(x)) != (v)) { \
    error(__FILE__,__LINE__, #x, t, (v)); \
}} while (0)

static void error(char *fn, int lno, char *s, long x, long v) {
    fprintf(stderr, "%s:%d %s returned %ld rather than %ld\n",
        fn, lno, s, x, v);
    exit(1);
}


void Lock(FairLock *f) {

    err(pthread_mutex_lock(&f->lock), 0);
    long me = f->scount++;
    while (f->wcount != me) {
        err(pthread_cond_wait(&f->cv, &f->lock), 0);
    }
    err(pthread_mutex_unlock(&f->lock), 0);

}

void UnLock(FairLock *f) {
    err(pthread_mutex_lock(&f->lock), 0);
    if (f->scount > f->wcount) {
        f->wcount++;
        err(pthread_cond_broadcast(&f->cv), 0);
    }
    err(pthread_mutex_unlock(&f->lock), 0);
}

FairLock *NewLock(void) {
    FairLock *p = malloc(sizeof *p);
    if (p != 0) {
        err(pthread_mutex_init(&p->lock, 0),0);
        err(pthread_cond_init(&p->cv, 0),0);
        p->scount = p->wcount = 0;
    }
    return p;
}

void DoneLock(FairLock *f) {
    err(pthread_mutex_destroy(&f->lock), 0);
    err(pthread_cond_destroy(&f->cv), 0);
}
并且您的testlock.c更改为使用它;同样还有改进的余地,但你应该能够坚持在任何地方睡觉,这将是公平的

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

#include "fairlock.c"

FairLock *my;

void *
my_thread(void *p)
{
  while (1) {
    Lock(my);
    printf("%s is holding the lock...\n", p);
    UnLock(my);
  }
}



int
main()
{
  pthread_t t;
  my = NewLock();
  pthread_create(&t, NULL, my_thread, "one");
  pthread_detach(t);
  pthread_create(&t, NULL, my_thread, "two");
  pthread_detach(t);
  pthread_create(&t, NULL, my_thread, "three");
  pthread_detach(t);
  pthread_create(&t, NULL, my_thread, "four");
  pthread_detach(t);
  pthread_create(&t, NULL, my_thread, "five");
  pthread_detach(t);

  while (1) {
    Lock(my);
    printf("main process is holding the lock...\n");
    UnLock(my);
  }
}
TLDR版本:

释放锁后,两个循环中的任何一个所做的下一件事就是再次尝试获取它

当刚刚释放锁的线程a和被阻塞的线程B在等待锁时发生竞争时,线程a几乎总是会赢,因为线程a已经在运行,而线程B仍然处于休眠状态

释放锁不会立即唤醒等待的线程。它所做的只是将另一个线程的状态从等待锁更改为等待分配 运行的中央处理器。不久之后,调度程序将着手在另一个CPU上恢复线程B的上下文,线程B将开始运行,但到那时已经太晚了。线程A将已重新锁定锁。

TLDR版本:

释放锁后,两个循环中的任何一个所做的下一件事就是再次尝试获取它

当刚刚释放锁的线程a和被阻塞的线程B在等待锁时发生竞争时,线程a几乎总是会赢,因为线程a已经在运行,而线程B仍然处于休眠状态


释放锁不会立即唤醒等待的线程。它所做的只是将另一个线程的状态从等待锁更改为等待分配一个CPU来运行。不久之后,调度程序将着手在另一个CPU上恢复线程B的上下文,线程B将开始运行,但到那时已经太晚了。线程A已经重新锁定了锁。

当调用线程阻塞等待释放锁时,不能保证它确实会获得锁,只能保证它会争夺锁。在主线程释放锁后,它会立即尝试重新获取,这似乎足够快,它会与另一个线程竞争,并且锁获取不能保证按顺序完成。释放后在主线程中添加一个微小的延迟可能会给您带来您想要的行为。当调用线程阻塞等待释放锁时,不能保证它确实会获得锁,只能保证它会竞争它。在主线程释放锁后,它会立即尝试重新获取,这似乎足够快,它会与另一个线程竞争,并且锁获取不能保证按顺序完成。发布后在主线程中添加一个微小的延迟可能会给您带来您想要的行为。@Shawn No.pthread_yield。如果你想要一个特定的订单,你必须自己编码。或者使用第三方库。:。。。pthread_产量从来都不是标准的。。。实际上,如果您依赖pthread_yield来做任何事情,那么您可能有一个逻辑问题。调度不同步;只是计划而已。。。它只是让另一个线程有机会获得锁。如果你想要严格的修改或其他什么,是的,这不合适。@Shawn不,这根本不能保证。从同一个链接:考虑pthRead屈服,只不过是一个调度器暗示,基本上说,哦,强大的调度器,难道你不认为你的智慧,你的可能从我的处理器在不久的将来,我恳求你现在做它。它可能会这样做,也可能会耸耸肩,继续做自己的事情。您不知道,您的代码无法判断@Shawn,Naw是否正常工作,这意味着有一些系统状态、负载、中断率等,以及会导致它失败的调度参数。@Shawn No.pthread_yield and。如果你想要一个特定的订单,你必须自己编码。或者使用第三方库。:。。。pthread_产量从来都不是标准的。。。实际上,如果您依赖pthread_yield来做任何事情,那么您可能有一个逻辑问题。调度不同步;只是计划而已。。。它只是让另一个线程有机会获得锁。如果你想要严格的修改或其他什么,是的,这不合适。@Shawn不,这根本不能保证。从同一个链接:考虑pthRead屈服,只不过是一个调度器暗示,基本上说,哦,强大的调度器,难道你不认为你的智慧,你的可能从我的处理器在不久的将来,我恳求你现在做它。它可能会这样做,也可能会耸耸肩,继续做自己的事情。您不知道,您的代码无法判断,@Shawn,Naw,是否正常工作,这意味着有一些系统状态负载、中断率等,调度参数会导致它失败。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct fairlock FairLock;
struct fairlock {
    pthread_mutex_t lock;
    pthread_cond_t  cv;
    long scount;
    long wcount;
};



#define err(x,v) do { int t; if ((t=(x)) != (v)) { \
    error(__FILE__,__LINE__, #x, t, (v)); \
}} while (0)

static void error(char *fn, int lno, char *s, long x, long v) {
    fprintf(stderr, "%s:%d %s returned %ld rather than %ld\n",
        fn, lno, s, x, v);
    exit(1);
}


void Lock(FairLock *f) {

    err(pthread_mutex_lock(&f->lock), 0);
    long me = f->scount++;
    while (f->wcount != me) {
        err(pthread_cond_wait(&f->cv, &f->lock), 0);
    }
    err(pthread_mutex_unlock(&f->lock), 0);

}

void UnLock(FairLock *f) {
    err(pthread_mutex_lock(&f->lock), 0);
    if (f->scount > f->wcount) {
        f->wcount++;
        err(pthread_cond_broadcast(&f->cv), 0);
    }
    err(pthread_mutex_unlock(&f->lock), 0);
}

FairLock *NewLock(void) {
    FairLock *p = malloc(sizeof *p);
    if (p != 0) {
        err(pthread_mutex_init(&p->lock, 0),0);
        err(pthread_cond_init(&p->cv, 0),0);
        p->scount = p->wcount = 0;
    }
    return p;
}

void DoneLock(FairLock *f) {
    err(pthread_mutex_destroy(&f->lock), 0);
    err(pthread_cond_destroy(&f->cv), 0);
}
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

#include "fairlock.c"

FairLock *my;

void *
my_thread(void *p)
{
  while (1) {
    Lock(my);
    printf("%s is holding the lock...\n", p);
    UnLock(my);
  }
}



int
main()
{
  pthread_t t;
  my = NewLock();
  pthread_create(&t, NULL, my_thread, "one");
  pthread_detach(t);
  pthread_create(&t, NULL, my_thread, "two");
  pthread_detach(t);
  pthread_create(&t, NULL, my_thread, "three");
  pthread_detach(t);
  pthread_create(&t, NULL, my_thread, "four");
  pthread_detach(t);
  pthread_create(&t, NULL, my_thread, "five");
  pthread_detach(t);

  while (1) {
    Lock(my);
    printf("main process is holding the lock...\n");
    UnLock(my);
  }
}