任务 1:编写程序 task31.c,主线程创建 3 个对等线程 T1、T2、T3,每个线程利用循环执行 5 次
printf 输出操作,两次循环间随机等待 1-5s 时间。主线程等待所有对等线程结束后终止进程。各对等
线程的输出操作是:
T1:输出“My name is <您的姓名 xxx>”
T2:输出“My student number is <您的学号 xxx>”
T3:输出“Current time <当前时间,包括年月日时分秒>
task31.c
#include<stdio.h>#include<stdlib.h>#include<time.h>#include<unistd.h>#include<pthread.h>void* thread_name(){for(int i=0;i<5;i++){printf("My name is \n");int time = rand()%5+1;sleep(time);}}void* thread_num(){for(int i=0;i<5;i++){printf("My student number is\n");int time = rand()%5+1;sleep(time);}}void* thread_time(){time_t now;for(int i=0;i<5;i++){struct tm *tm_now;time(&now);tm_now = localtime(&now);printf("Current time is: %d-%d-%d %d:%d:%d\n",tm_now->tm_year+1900, tm_now->tm_mon+1, tm_now->tm_mday, tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec);int time = rand()%5+1;sleep(time);}}int main(){pthread_t t1,t2,t3;pthread_create(&t1,NULL,thread_name,NULL);pthread_create(&t2,NULL,thread_num,NULL);pthread_create(&t3,NULL,thread_time,NULL);pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_join(t3, NULL); }
gcc task31.c -o task31 -pthread
./task31
任务 2:
编译、测试和运行图 6-13 示例程序 badcount.c,请通过测试找到程序运行开始出错的 niters 的最小值,并解释出错原因。用 pthread 信号量方法改写程序 badcount.c,保存为 task62.c,实现对共享变量的安全访问,并进行测试验证。
task32.c
#include <stdio.h>#include <pthread.h>#include <stdlib.h>#include <semaphore.h>void *increase(void *arg);void *decrease(void *arg);unsigned int cnt = 0;pthread_mutex_t mutex;int main(int argc, char **argv) {unsigned int niters;pthread_t tid1, tid2;if(argc!=2) {printf("usage:%s <niters>\n",argv[0]); exit(2);}pthread_mutex_init(&mutex,NULL);niters=atoll(argv[1]); pthread_create(&tid1, NULL, increase, (void*)&niters);pthread_create(&tid2, NULL, decrease, (void*)&niters);pthread_join(tid1, NULL);pthread_join(tid2, NULL);if (cnt != (unsigned int)0)printf("Error! cnt=%d\n", cnt);elseprintf("Correct cnt=%d\n", cnt);exit(0);}void *increase(void *vargp) {unsigned int i,niters=*(unsigned int* )vargp;for (i = 0; i < niters; i++){pthread_mutex_lock(&mutex);cnt++;pthread_mutex_unlock(&mutex);}return NULL;}void *decrease(void *vargp) {unsigned int i,niters=*(unsigned int *)vargp;for (i = 0; i < niters; i++){pthread_mutex_lock(&mutex);cnt--;pthread_mutex_unlock(&mutex);}return NULL;}
用 niters=10n 进行测试,给出 badcount.c 开始出错的 n 最小值,解释出错原因;
n=5时开始出错,赋值语句是由多条汇编语句组成的,当数量过多时可能出现错误
任务 3:编写一个多线程程序 task33.c,创建 k 个生产者线程和 m 个消费者线程,每个生产者线程 产生若干个随机数,通过由 N 个单元构成的缓冲区,发送给消费者线程,进行输出显示,产生 pthread 信号量实现生产者/消费者线程间同步,并且设计一种方案对程序正确性进行验证。
提示:一种非严谨的简单验证方案是,将生产者线程产生的所有随机数相加得到一个和,消费者
线程接收到的所有随机数相加得到另一个和,验证两个和是否一致来来验证程序正确性。
task33.c
#include<stdio.h>#include<stdlib.h>#include<pthread.h>#include<semaphore.h>#include<stdbool.h>#define N 20#define K 10#define M 20int buf[N];//sum_out记录生产者生产总数,sum_in记录消费者接收总数int outpos = 0,inpos = 0,sum_out=0,sum_in=0;sem_t avail,ready;pthread_mutex_t out,in;void sbuf_insert(int item){buf[inpos]=item;inpos=(inpos+1)%N;}int sbuf_remove(){int item=buf[outpos];outpos=(outpos+1)%N;return item; }void *Thread_P(){bool flag=true;while(flag){int i = rand()%100;sem_wait(&avail);pthread_mutex_lock(&in);sbuf_insert(i);sum_out+=i;if(sum_out >= 666666){//生产总数达到一定数值后结束线程flag=false;printf("we product %d\n",sum_out);}pthread_mutex_unlock(&in);sem_post(&ready);}}void *Thread_C(){while(1){sem_wait(&ready);pthread_mutex_lock(&out);int i = sbuf_remove();sum_in+=i;printf("we receive %d\n",sum_in);pthread_mutex_unlock(&out);sem_post(&avail);}}int main(){sem_init(&avail,0,N);sem_init(&ready,0,0);pthread_mutex_init(&out,NULL);pthread_mutex_init(&in,NULL);pthread_t pro[K];//生产者pthread_t cus[M];//消费者for(int i=0;i<K;i++){pthread_create(&pro[i],NULL,Thread_P,NULL);}for(int i=0;i<M;i++){pthread_create(&cus[i],NULL,Thread_C,NULL);}for(int i=0;i<K;i++){pthread_join(pro[i],NULL);}for(int i=0;i<M;i++){pthread_join(cus[i],NULL);}return 0;}
需要手动Ctrl+c结束进程,不然消费者进程会一直等待唤醒,而此时生产者进程已经结束不可能唤醒消费者进程。
任务 4:编译、测试和运行示例程序 psum64.c 1)测量线程数为 1、2、4、8、16 时程序的执行时间,计算加速比和效率,并做出解释。 2)改写该程序 psum64.c,保存为 task34.c,实现计算 02+12+… +(n-1)2 功能。
tsak34.c
#include <stdio.h>#include <stdlib.h>#include <pthread.h>#define MAXTHREADS 32void *sum(void *vargp);/* Global shared var��ables */unsigned long long psum[MAXTHREADS]; /* Partial sum computed by each thread */unsigned long long nelems_per_thread; /* Number of elements summed by each thread */int main(int argc , char **argv){unsigned long long i , nelems , log_nelems , nthreads , result = 0;pthread_t tid[MAXTHREADS];int myid[MAXTHREADS];/*Get input arguments */if (argc != 3) {printf ("Usage: %s <nthreads> <log_nelems>\n" , argv [0]) ;exit(0);}nthreads = atoi(argv[1]) ;log_nelems = atoi(argv[2]);nelems = (1L << log_nelems);nelems_per_thread = nelems / nthreads;/* Create peer threads and wait for them to f��nish */for (i = 0; i < nthreads; i++) {myid [i] = i;pthread_create (&tid[i],NULL, sum, &myid[i]);} for (i = 0; i < nthreads; i++)pthread_join(tid[i] ,NULL) ;/* Add Up the partial sums computed by each thread */for (i = 0; i < nthreads; i++)result += psum[i];/* Check final answer */if (result == (nelems*(nelems-1)*(2*nelems-1)/6))printf("Correct Result=%lld\n",result);elseprintf("Error: result=%lld\n" , result);exit(0);}void *sum(void* argp){int myid = *((int*)argp); /* Extract the thread ID */unsigned long long start = myid *nelems_per_thread; /* Start element index */unsigned long long end = start + nelems_per_thread; /* F.nd element index */unsigned long long i, lsum= 0;for (i = start; i < end; i++) {lsum += i*i;}psum[myid] = lsum;return NULL;}
nthreads = atoi(argv[1]) ;//获取第一个参数作为线程总数 log_nelems = atoi(argv[2]);//第二个参数作为2的指数幂 nelems = (1L << log_nelems);//加到2的 log_nelems次方为止 nelems_per_thread = nelems / nthreads;//分配每个线程执行多少 例如当传进参数2 和10: 从0加到2¹⁰-1,分给两个线程执行,每个线程执行2⁵个数字的加法
要求:计算不同线程数时的性能,填写以下表格,并对运行时间和加速比进行解释:
关于加速比和效率可参考文章 加速比
单处理器下的时间即只运行一个线程所用的时间