多个线程同时访问共享数据时可能会冲突。比如两个线程都要把某个全局变量增加1,这个操作在某平台需要三条指令完成:
1.从内存读变量值到寄存器
2.寄存器的值加1
3.将寄存器的值写回内存
假设两个线程在多处理器平台上同时执行这三条指令,则可能导致上图所示的结果,最后变量只加了一次而非两次
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define NLOOP 1000
int counter; /* incremented by threads */
void *doit(void *);
int main(int argc, char **argv)
{
pthread_t tidA, tidB;
pthread_create(&tidA, NULL, &doit, NULL);
pthread_create(&tidB, NULL, &doit, NULL);
/* wait for both threads to terminate */
pthread_join(tidA, NULL);
pthread_join(tidB, NULL);
return 0;
}
void *doit(void *vptr)
{
int i, val;
for (i = 0; i < NLOOP; i++)
{
val = counter;//1
printf("%x: %d\n", (unsigned int)pthread_self(), val + 1);//2
counter = val + 1;//3
}
return NULL;
}
用注释中的1,2,3模拟三步指令
实验结果:
....
3dcac700: 1011
3dcac700: 1012
3dcac700: 1013
3dcac700: 1014
3dcac700: 1015
3dcac700: 1016
3dcac700: 1017
3dcac700: 1018
3dcac700: 1019
3dcac700: 1020
3dcac700: 1021
并没有加到2000
线程为什么要同步
1.共享资源,多个线程都可对共享资源操作
2.线程操作共享资源的先后顺序不确定
3.处理器对存储器的操作一般不是原子操作
解决办法:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define NLOOP 1000
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int counter; /* incremented by threads */
void *doit(void *);
int main(int argc, char **argv)
{
pthread_t tidA, tidB;
pthread_create(&tidA, NULL, &doit, NULL);
pthread_create(&tidB, NULL, &doit, NULL);
/* wait for both threads to terminate */
pthread_join(tidA, NULL);
pthread_join(tidB, NULL);
return 0;
}
void *doit(void *vptr)
{
int i, val;
for (i = 0; i < NLOOP; i++)
{
pthread_mutex_lock(&mutex);
val = counter;
printf("%x: %d\n", (unsigned int)pthread_self(), val + 1);
counter = val + 1;
pthread_mutex_unlock(&mutex);
}
return NULL;
}
死锁:
1.同一个线程在拥有A锁的情况下再次请求获得A锁
2.线程一拥有A锁,请求获得B锁;线程二拥有B锁,请求获得A锁死锁导致的结果是什么?
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
pthread_mutex_t mutexA = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutexB = PTHREAD_MUTEX_INITIALIZER;
int counter; /* incremented by threads */
void *doitA(void *);
void *doitB(void *);
int main(int argc, char **argv)
{
pthread_t tidA, tidB;
pthread_create(&tidA, NULL, &doitA, NULL);
pthread_create(&tidB, NULL, &doitB, NULL);
/* wait for both threads to terminate */
pthread_join(tidA, NULL);
pthread_join(tidB, NULL);
return 0;
}
void *doitA(void *vptr)
{
pthread_mutex_lock(&mutexA);
printf("doitA:after lock A!\n");
sleep(1);
printf("doitA:before lock B!\n");
pthread_mutex_lock(&mutexB);
pthread_mutex_unlock(&mutexB);
pthread_mutex_unlock(&mutexA);
return NULL;
}
void *doitB(void *vptr)
{
pthread_mutex_lock(&mutexB);
printf("after lock B!\n");
sleep(1);
printf("before lock A!\n");
pthread_mutex_lock(&mutexA);
pthread_mutex_unlock(&mutexA);
pthread_mutex_unlock(&mutexB);
return NULL;
}