操作系统一实验3
线程
■目的
深入理解线程及线程在调度执行和内存空间等方面的特点, 并掌握线程与进程的区别。掌握POSIX规范中pthread create( )函数的功能和使用方法。
■实验前准备
阅读参考资料,了解线程的创建等相关系统调用
实验准备:
线程创建函数:pthread_create(&pthread_id[i] , NULL , thread_work , ( void* ) & ( index[ i ] ) );
第一个参数:把创建出来的线程的编号传给pthread_id[ i ];
第二个参数:一般为NULL;
第三个参数:简单理解为该线程执行什么样的工作。
第四个参数:一般来说把第四个参数传到第三个参数中。
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<ctype.h>
#include<pthread.h>
#define MAX_THREAD 3
unsigned long long main_counter,counter[MAX_THREAD];
void* thread_work(void*);
int main(int argc,char*argv[])
{
int i,rtn,ch;
int index[MAX_THREAD];
pthread_t pthread_id[MAX_THREAD] = {0};
for(int i = 0 ; i < MAX_THREAD;i++)
{
index[i] = i;
pthread_create(&pthread_id[i],NULL,thread_work,(void *)&(index[i]));//把整数类型的地址转换成void类型的地址,这样能传到 void* thread_work(void*)中。
}
do
{
unsigned long long sum = 0;
for(int i = 0 ; i < MAX_THREAD;i++)
{
sum+=counter[i];
printf("%llu ",counter[i]);
printf("%llu/%llu",main_counter,sum);
}
}while((ch = getchar()) != 'q');
return 0;
}
void *thread_work(void *p)
{
int thread_num = *((int *)p);
for(;;)
{
counter[thread_num]++;
main_counter++;
}
}
运行结果:
程序解读:
(1)程序执行之后,进程有一个主线程,三个子线程。程序执行完,主线程结束,三个子线程会强行结束。
(2)之所以最后main_counter和三个counter[ i ]不同,是因为三个线程共享一个main_counter,导致并发执行
main_counter++,但是每个counter[ i ]是正常加,所以main_counter比三个counter[ i ]相加要小。
(3)三个counter[ i ]占用CPU的效率近似相同,但并不完全相同,与时间片轮转有关。
正确代码:
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<ctype.h>
#include<pthread.h>
#define MAX_THREAD 3 //线程个数
//初始化锁
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
unsigned long long main_counter,counter[MAX_THREAD]={0};
void* thread_worker(void* arg)
{
//先将void* 转为 int* 再赋值
int thread_num = *(int*)arg;
//释放内存
free((int*)arg);
for(;;)
{
//加锁
pthread_mutex_lock(&mutex);
counter[thread_num]++; //本线程的counter 加 1
main_counter++;
//解锁
pthread_mutex_unlock(&mutex);
//sleep(3);
}
}
int main(int argc,char* argv[])
{
int i,rtn,ch;
pthread_t pthread_id[MAX_THREAD] = {0}; //存放线程
int *param;
for(i=0;i<MAX_THREAD;i++)
{
//申请内存临时保存参数
param = (int*)malloc(sizeof(int));
*param = i;
pthread_create(&pthread_id[i],NULL,thread_worker,param);
}
do
{
//加锁
pthread_mutex_lock(&mutex);
unsigned long long sum = 0;
for(i=0;i<MAX_THREAD;i++)
{
sum += counter[i];
printf("No.%d: %llu\n",i,counter[i]);
}
printf("%llu/%llu\n",main_counter,sum);
//解锁
pthread_mutex_unlock(&mutex);
}while((ch = getchar())!='q');
//销毁锁资源
pthread_mutex_destroy(&mutex);
return 0;
}
这里解释一下两个加锁的地方:
1.第一个加锁的地方在工作函数,如果工作函数不加锁,3个子线程会竞争一个main_counter资源,导致数据会和3个counter之和不同,main_counter比较小。
2.第二个加锁的地方在do_while循环里面,这里如果不加虽然主线程已经加过3个counter,但是子线程还在运行,main_counter还在增加。导致main_counter比较大