My C program has three threads A
, B
, and C
that share some buffers. Each buffer is protected by a mutex, and each thread has to lock the mutex before writing/reading a buffer.
我的C程序有三个线程A、B和C,它们共享一些缓冲区。每个缓冲区都有一个互斥锁保护,每个线程在写/读缓冲区之前都必须锁定互斥锁。
A
writes the buffers, and B
and C
reads the buffers. The main function creates A
, waits until it locks the first buffer with pthread_mutex_lock()
, and then creates B
and C
, so that the buffers are always written before being read.
A写入缓冲区,B和C读取缓冲区。Main函数创建A,等待直到它使用pthreadmutex_lock()锁定第一个缓冲区,然后创建B和C,以便总是在读取缓冲区之前写入缓冲区。
#define mutex_lock(mutex, buff_num) log("wait on buff[%d]\n", buff_num); \
printf("pthread_mutex_lock returns %d\n", pthread_mutex_lock(mutex)); \
log("buff[%d] locked\n", buff_num)
#define mutex_unlock(mutex, buff_num) log("releasing buff[%d]\n", buff_num); \
pthread_mutex_unlock(mutex); \
log("buff[%d] released\n", buff_num)
...
typedef struct
{
pthread_mutex_t lock;
char data[MAX_DATA_LENGTH];
} buff_t;
uint8_t A_thread_ready = 0;
buff_t buff[NUM_BUFF];
...
int main()
{
...
for(int i=0; i<NUM_BUFF; i++)
{
if (pthread_mutex_init(&buff[i].lock, NULL) != 0)
{
pthread_exit(NULL);
}
}
...
status = pthread_create(&tid[0], NULL, &A_thread, NULL);
if ( 0 == status)
{
break;
}
do
{
nanosleep(&t1, &t2);
} while (0 == A_thread_ready);
status = pthread_create(&tid[1], NULL, &B_thread, NULL);
if ( 0 == status)
{
break;
}
...
}
...
extern uint8_t A_thread_ready;
extern buff_t buff[NUM_BUFF];
...
void* A_thread(void* arg)
{
uint8_t write_buff = 0;
...
buff_p = &(buff[write_buff]);
mutex_lock(&buff_p->lock, write_buff);
A_thread_ready = 1;
while(1)
{
...
mutex_unlock(&buff_p->lock, write_buff);
write_buff++;
write_buff %= NUM_BUFF;
buff_p = &(buff[write_buff]);
mutex_lock(&buff_p->lock, write_buff);
...
}
...
}
...
extern buff_t buff[NUM_BUFF];
...
void* B_thread(void* arg)
{
uint8_t read_buff = 0;
...
buff_p = &(buff[read_buff]);
mutex_lock(&buff_p->lock, read_buff);
while(1)
{
...
mutex_unlock(&buff_p->lock, read_buff);
read_buff++;
read_buff %= NUM_BUFF;
buff_p = &(buff[read_buff]);
mutex_lock(&buff_p->lock, read_buff);
...
}
...
}
When a thread was locking a mutex, the three logs always appeared in three successive lines: the log waiting on the mutex before the printf()
in the macro, pthread_mutex_lock()
returned 0 (denoting a success), and the log showing buffer locked right after the printf()
.
当线程锁定互斥锁时,三个日志总是出现在三个连续的行中:在宏中的printf()之前等待互斥锁的日志,pthreadmutex_lock()返回0(表示成功),而在printf()之后显示缓冲区锁定的日志。
When running the program, however, from the printed logs, it seems that accessing to a buffer is not exclusive: after A
locked a buffer, B
was able to lock this same buffer. It then did something, and released the buffer. After some time A
released this buffer. What else should I check to find the cause of this problem?
然而,当运行程序时,从打印的日志来看,对缓冲区的访问似乎并不是独占的:在A锁定缓冲区之后,B也能够锁定相同的缓冲区。然后它做了一些事情,并释放了缓冲区。一段时间后,A释放了该缓冲区。我还应该检查什么才能找到这个问题的原因?
更多回答
This needs a minimal reproducible example that people can compile, run, and test; not a bunch of snippets with ... in between.
这需要一个人们可以编译、运行和测试的最小的可重现的示例;而不是一堆带有...介于两者之间。
You have a data race on A_thread_ready
. It either needs to be _Atomic
, or it needs a mutex of its own to protect it. I don't know if that's related to your issue, but strictly speaking, it makes the entire program have undefined behavior.
在A_THREAD_READY上有一场数据竞赛。它要么需要是原子的,要么需要自己的互斥体来保护它。我不知道这是否与您的问题有关,但严格地说,它使整个程序具有未定义的行为。
Moreover, testing A_thread_ready
does not provide assurance that A_thread
has actually taken its lock; a non-atomic variable cannot provide synchronization.
此外,测试A_THREAD_READY并不能保证A_THREAD确实取得了它的锁;非原子变量不能提供同步。
The phrase it seems that accessing to a buffer is not excessive doesn't mean anything to me. Did you mean access to a buffer is not exclusive? Also, can you please cut and paste the portion of the log messages that you find unexpected? As your code is currently written, you may very well see buff[1] locked
(by A) followed by buff[1] released
(by B). Even though the locking is correct because wait on buff
and buff[%d] released
are logged outside the mutex-guarded code. You may just have some personal incredulity and be drawing the wrong conclusions from your observations.
访问缓冲区似乎并不过分这句话对我来说没有任何意义。您的意思是访问缓冲区不是独占的?另外,您可以剪切并粘贴您认为意外的日志消息部分吗?在您当前编写代码时,您很可能会看到buff[1]被锁定(由A),然后buff[1]被释放(由B)。即使锁定是正确的,因为等待缓冲区和释放的缓冲区[%d]记录在互斥保护代码之外。你可能只是有一些个人的怀疑,从你的观察中得出了错误的结论。
@fiedel If you have a while(1)
somewhere in main
it should be part of the posted code. As it is now, it's very confusing. Actually, you should post code that we can copy, compile and test directly.
@Fiedel如果你在Main中的某个地方有一个While(1),它应该是发布的代码的一部分。就像现在一样,这非常令人困惑。实际上,你应该发布我们可以直接复制、编译和测试的代码。
我是一名优秀的程序员,十分优秀!