好的,所以我正在尝试编写一个基本程序,它将创建共享资源的“读者”和“编写者”线程,并尽可能优先于作者。我使用互斥结构保护此资源,但代码的行为不是我所期望的。我已将问题追溯到我的代码的一行,但我无法理解为什么它不起作用! “bug”并不总是发生,这是线程所期望的,但是当它确实发生时,我找不到合适的解释! 这是我的计划:
Function GetIPAddress: String;
type pu_long = ^u_long;
var varTWSAData : TWSAData;
varPHostEnt : PHostEnt;
varTInAddr : TInAddr;
namebuf : Array[0..255] of ansichar;
begin
try
try
If WSAStartup($101,varTWSAData) <> 0 Then
Result := ''
Else Begin
gethostname(namebuf,sizeof(namebuf));
varPHostEnt := gethostbyname(namebuf);
varTInAddr.S_addr := u_long(pu_long(varPHostEnt^.h_addr_list^)^);
Result := inet_ntoa(varTInAddr);
End;
except
Result := '';
end;
finally
WSACleanup;
end;
end;
查看该行(在reader_job中)reader_queue_length ++;,我不明白为什么当该行在互斥锁之外时该程序不起作用,但是当它在内部时它可以工作!输出让我更加困惑,因为“bug”发生在最后(在这个特定的运行实例中,见下文),当所有线程都已经加入时,因此没有人留下来改变我的变量,对吧?! / p>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#define N 3 //Number of readers.
#define M 3 //Number of writers.
#define X 2 //Number of times a reader reads.
#define Y 2 //Number of times a writer writes.
pthread_mutex_t res_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t res_cond_read = PTHREAD_COND_INITIALIZER;
pthread_cond_t res_cond_write = PTHREAD_COND_INITIALIZER;
int resource = 0, state = 0 /*1 = writing, 0 = waiting, abs(<0) = #readers*/, reader_queue_length = 0;
void *reader_job (void* parg);
void *writer_job (void* parg);
int main ()
{
srandom((unsigned int)time(NULL));
pthread_t readers[N];
pthread_t writers[M];
int valueR[N], valueW[M];
//printf ("NOTICE: Reader queue lengths excluding current thread.\n");
for (int i=0; i<N; i++) {
valueR[i] = i;
}
for (int i=0; i<M; i++) {
valueW[i] = i;
}
for (int i=0; i<N; i++) {
pthread_create (&readers[i], NULL, reader_job, &valueR[i]);
}
for (int i=0; i<M; i++) {
pthread_create (&writers[i], NULL, writer_job, &valueW[i]);
}
for (int i=0; i<N; i++) {
pthread_join (readers[i], NULL);
}
for (int i=0; i<M; i++){
pthread_join (writers[i], NULL);
}
return 0;
}
void *reader_job (void* parg) {
int *temp = (int*) parg;
int value = *temp;
int local_read_num = 0;
printf (">>> Creating reader: %d\n", value);
while (local_read_num < X) {
usleep(1000 * (random() % (N+M)));
reader_queue_length++; //When this line is here, wrong.
printf ("INFO::About to access entry mutex for reader with ID: %2d, with queue length %2d.\n",
value, reader_queue_length);
pthread_mutex_lock(&res_mutex);
//reader_queue_length++; //When it is here, right.
if (reader_queue_length == 0) printf ("\nERROR::Before entry mutex!\n\n");
while (state != 0)
pthread_cond_wait (&res_cond_read, &res_mutex);
state--;
if (reader_queue_length == 0) printf ("\nERROR::During entry mutex!\n\n");
reader_queue_length--;
printf ("INFO::About to exit entry mutex for reader with ID: %2d, with queue length %2d.\n",
value, reader_queue_length);
pthread_mutex_unlock (&res_mutex);
printf ("[READER (ID: %2d):] Readers in queue: %2d, read: %2d\n", value, reader_queue_length, resource);
pthread_mutex_lock (&res_mutex);
state++;
pthread_cond_signal (&res_cond_read);
if (reader_queue_length == 0) pthread_cond_signal (&res_cond_write);
pthread_mutex_unlock (&res_mutex);
local_read_num++;
}
printf ("!!! Destroying reader: %d\n", value);
pthread_exit (NULL);
}
void *writer_job (void* parg) {
int *temp = (int*) parg;
int value = *temp;
int local_write_num = 0;
printf (">>> Creating writer: %d\n", value);
while (local_write_num < Y) {
//usleep(1000 * (random() % (N+M)));
pthread_mutex_lock(&res_mutex);
while (state != 0 || reader_queue_length > 0)
pthread_cond_wait (&res_cond_write, &res_mutex);
state++;
pthread_mutex_unlock(&res_mutex);
resource = value*5+local_write_num;
printf ("[WRITER (ID: %2d):] Readers in queue: %2d, wrote: %2d\n", value, reader_queue_length, resource);
pthread_mutex_lock (&res_mutex);
state--;
pthread_mutex_unlock(&res_mutex);
pthread_cond_signal(&res_cond_read);
pthread_cond_signal(&res_cond_write);
local_write_num++;
}
printf ("!!! Destroying writer: %d\n", value);
pthread_exit (NULL);
}
读者队列如何以负长度结束?哪个线程可以改变内容?它们都已经被破坏了,或者至少超过了它们可以搞乱变量的运行周期中的点!
仅供参考我在Ubuntu 16.04上使用gcc编译器和-pthread标志运行它。
提前谢谢大家!
答案 0 :(得分:1)
好吧,所以我认为这就是问题:当我尝试在互斥锁之外编辑队列长度变量时,会发生多个线程尝试一次访问同一个变量,导致数据竞争!
例如,如果变量的值为A,并且尝试访问它的命令尝试将值编辑为A + 1,则可能是尝试访问变量的线程全部加载初始值A,没有正确地等待彼此编辑变量然后获取更新的值。因此,在进入互斥锁之前,它们都会以值A + 1结束,这会导致“错误”。
底线:该命令需要在互斥锁内!