1.运行程序,说明该程序的目的。
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#define N 2
void *thread(void *vargp);
char **ptr; /* 全局变量 */
int main()
{
int i;
pthread_t tid;
char *msgs[N] = {
"Hello from foo",
"Hello from bar"
};
ptr = msgs;
for (i = 0; i < N;i++)
{
pthread_create(&tid, NULL, thread, (void *)i);
// pthread_join(tid, NULL);
}
//tid指向新创建线程ID的变量,作为函数的输出
//NULL表示不同线程的属性为默认
//函数指针, 为线程开始执行的函数名
//函数的唯一无类型(void)指针参数, 如要传多个参数, 可以用结构封装
pthread_exit(NULL);
}
void *thread(void *vargp)
{
int myid = (int)vargp;
static int cnt = 0;
printf("[%d]: %s (cnt=%d)\n", myid, ptr[myid], ++cnt);
return NULL;
}
A.填表:其中主线程叫m,其它两个线程叫p0、p1。i.m的意思表示i驻留在主线程的本地栈中
变量实例 | 主线程引用否? | 0线程引用否? | 1线程引用否 |
ptr |
|
|
|
cnt |
|
|
|
i.m |
|
|
|
msgs.m |
|
|
|
myid.p0 |
|
|
|
myid.p1 |
|
|
|
根据A中的填写:变量ptr, cnt, msgs被多于一个线程引用,所以它们是共享的。
(1). ptr表示全局变量,表示定义在函数之外的变量。在运行时,只存在一个实例,任何线程都可以引用,故可共享。
(2). cnt表示本地静态变量,在运行时,虚拟内存的读/写区域只有一个实例,对等线程均读写该实例。故在子线程中可共享。
(3). i的作用域只在主线程中,在传参的时候也没有传入指针,只传值,其他线程无法引用,非共享。
(4). 其他线程可以通过可以通过ptr全局指针来访问msgs,为共享变量。
(5). myid是本地自动变量,每个线程的栈都包含它自己的所有本地自动变量的实例。故不共享
2.运行该程序,这个程序的预期结果是2*NITERS,但是结果会有多种。请利用信号灯及pv操作修改这个程序,使其达到预期。
#include <unistd.h>
#include <pthread.h>
void * thread(void * vargp); /* Thread routine prototype */
/* Global shared variable */
volatile int cnt = 0; /* Counter */
int main(int argc, char ** argv) {
int niters;
pthread_t tid1, tid2;
/* Check input argument */
if (argc != 2) {
printf("usage: %s <niters>\n", argv[0]);
exit(0);
}
niters = atoi(argv[1]);
/* Create threads and wait for them to finish */
pthread_create( & tid1, NULL, thread, & niters);
pthread_create( & tid2, NULL, thread, & niters);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
/* Check result */
if (cnt != (2 * niters)) printf("BOOM! cnt=%d\n", cnt);
else printf("OK cnt=%d\n", cnt);
exit(0);
}
/* Thread routine */
void * thread(void * vargp) {
int i, niters = * ((int * ) vargp);
for (i = 0; i < niters; i++)
cnt++;
return NULL;
}
预期的结果会不同的原因是因为创建的两个线程均要使用同一个变量实例cnt,但是对cnt变量进行操作的时候,由于线程的进行是并发的,并且速度很快,在执行cnt++时,汇编代码需要操作三步(1).加载cnt (2).更新cnt (3).存储cnt。在这之中如果两个线程的顺序交错了,就可能出现最后的cnt值和预期的不一样。
使用信号灯修改这个程序,可以在cnt++前后使用PV操作,防止上述情况出现。
void *thread(void *vargp)
{
//pthread_t tid = pthread_self();
sem_t mutex;//定义互斥信号量
sem_init(&mutex, 0, 1);//初始化mutex=1
//printf("tid: %d\n ", tid);
int i, niters = *((int *)vargp);
for (i = 0; i < niters; i++)
{
sem_wait(&mutex);//P
cnt++;
sem_post(&mutex);//V
}
return NULL;
}