在线程这一章节里感觉比进程更然了,进程呢一般你创建了他,他就会继承父进程,然后单独再开辟一块空间。在自己的地盘上做操作,都不会影响其他进程。而线程就不一样了,如果你使用了全局变量。他由于调度问题,你胡乱改,出现各种意想不到结果。灰常坑。。。
一下总结几个代码,记录一些坑点。。。
- 1
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <ctype.h>
#include<stdlib.h>
/* 允许建立的子进程个数最大值 */
#define MAX_CHILD_NUMBER 10
/* 子进程睡眠时间 */
#define SLEEP_INTERVAL 2
int proc_number=0;
/* 子进程的自编号,从0开始 */
void do_something();
int main(int argc, char* argv[])
{
/* 子进程个数 */
int child_proc_number = MAX_CHILD_NUMBER;
int i, ch;
pid_t child_pid;
pid_t pid[10]={0}; /* 存放每个子进程的id */
if (argc > 1) /* 命令行参数第一个参数表示子进程个数*/
{
child_proc_number = atoi(argv[1]);
child_proc_number= (child_proc_number > 10) ? 10 :child_proc_number;//保证最大线程为十个;
}
for (i=0; i<child_proc_number; i++)
{
/* 填写代码,建立child_proc_number个子进程要执行
* proc_number = i;
* do_something();
* 父进程把子进程的id保存到pid[i] */
pid[i]=fork();
proc_number = i;
if(pid[i]==0)
{
do_something();
exit (0); //这个应该是不可达的;
}
}
/* 让用户选择杀死进程,数字表示杀死该进程,q退出 */
while ((ch = getchar()) != 'q')
{
if (isdigit(ch))
{
/* 填写代码,向pid[ch-'0']发信号SIGTERM,
* 杀死该子进程 */
printf("**********%d***\n",pid[ch-'0']);
if(pid[ch-'0']==-1)
{
printf("这个进程早死了\n");
}
else
{
signal(SIGCHLD,SIG_IGN);
if(kill(pid[ch-'0'],SIGKILL)==-1)
printf("失败!!!!!!!!!!!1\n");
else
pid[ch-'0']=-1;
}
}
}
/* 在这里填写代码,杀死本组的所有进程 */
for (i=0; i<child_proc_number; i++)
{
if(pid[i]!=-1)
{
signal(SIGCHLD,SIG_IGN);
kill(pid[ch-'0'],SIGKILL);
pid[ch-'0']=-1;
}
}
return 0;
}
void do_something()
{
for(;;)
{
printf("This is process No.%d and its pid is %d\n",proc_number, getpid());
sleep(SLEEP_INTERVAL); // 主动阻塞两秒钟
}
}
这个程序是叫你创建不大于10个子进程(大于是个自动设定为十个)。然后输入编号就可以杀死对应进程。这里注意一点就是如果杀死了,就把他标记为-1,避免再次kill。还有一点是如果直接调用kill会产生僵尸进程。所以在kill之前用signal(SIGCHLD,SIG_IGN);忽视掉子进程的返回,把子进程返回交给系统释放。
- 2
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <ctype.h>
#include <pthread.h>
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
#define MAX_THREAD 3 /* 线程的个数 */
unsigned long long main_counter, counter[MAX_THREAD];
/* unsigned long long是比long还长的整数 */
void* thread_worker(void*);
int main(int argc,char* argv[])
{
int i, rtn, ch;
int a[3];int k=0;
pthread_t pthread_id[MAX_THREAD] = {0}; /* 存放线程id*/
for (i=0; i<MAX_THREAD; i++)
{
/* 在这里填写代码,用pthread_create建一个普通的线程,
线程id存入pthread_id[i],线程执行函数是thread_worker
并i作为参数传递给线程 */
a[k]=i;
pthread_create(&pthread_id[i],NULL,thread_worker,&a[k]);
k++;
}
do
{
/* 用户按一次回车执行下面的循环体一次。按q退出 */
unsigned long long sum = 0;
pthread_mutex_lock(&mutex1);
/* 求所有线程的counter的和 */
for (i=0; i<MAX_THREAD; i++)
{
/* 求所有counter的和 */
sum += counter[i];
printf("%llu ", counter[i]);
}
printf("%llu/%llu", main_counter, sum);
pthread_mutex_unlock(&mutex1);
}while ((ch = getchar()) != 'q');
return 0;
}
void* thread_worker(void *p)
{
int thread_num;
/* 在这里填写代码,把main中的i的值传递给thread_num */
thread_num=*(int*)p;
printf("***%d\n",thread_num);
for(;;)
{ /* 无限循环 */
pthread_mutex_lock(&mutex1);
counter[thread_num]++; /* 本线程的counter加一 */
main_counter++; /* 主counter 加一 */
pthread_mutex_unlock(&mutex1);
}
}
/*每个线程在争夺资源,*/
这个程序就灰常有意思,坑点特别多。
第一,在主进程循环创建子线程的时候,如果我们传参只是传了i的地址。那么主线程可能会在子线程给thread_num赋值的时候就已经对i进行过多次操作了。所以我们再开一个数组把当前的i及时从在数组不同元素下表中,然后传参时传相应的数组元素地址就可以了。
第二,三个子线程都在对一个全局变量main_counter++;。我们知道,一条语句在计算机执行的时候是分为多个指令的。如果三个进程先后将main_counter的值取出来(假如三个线程都取得了的是1),先后对值进行加一操作(三个线程都将取得的1加为2),再先后把加了的值赋给main_counter(三个线程依次将2赋值给main_counter)。那么,本来是想加三次,结果少加了,那么我们可以看到main_counter的值要比三个线程之和要小。所以我们要在循环加的地方上一把锁,避免三个线程同时对main_counter进行操作;
第三,在主线程的sum对三个counter[i]累加的时候,三个线程也会是并发的,也就是说sum在加的时候只是记录了当前值。在这之后又会改变。所以main_counter比sum大。所以我们再从加到输出上一把锁,这样counter[i]和main_counter在加到输出的时候是不会变的。所以sum和main_counter就是一样的了。
- 3
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <ctype.h>
#include <pthread.h>
#define LOOP_TIMES 10000
/*用宏PTHREAD_MUTEX_INITIALIZER来初始化 */
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
void* thread_worker(void*);
void critical_section(int thread_num, int i);
int main(void)
{
int rtn, i;
pthread_t pthread_id = 0; /* 存放子线程的id */
rtn = pthread_create(&pthread_id,NULL, thread_worker, NULL );
if(rtn != 0)
{
printf("pthread_create ERROR!\n");
return -1;
}
for (i=0; i<LOOP_TIMES; i++)
{
pthread_mutex_lock(&mutex1);
pthread_mutex_lock(&mutex2);
critical_section(1, i);
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex1);
}
pthread_mutex_destroy(&mutex1);
pthread_mutex_destroy(&mutex2);
return 0;
}
void* thread_worker(void* p)
{
int i;
for (i=0; i<LOOP_TIMES; i++)
{
pthread_mutex_lock(&mutex2);
pthread_mutex_lock(&mutex1);
critical_section(2, i);
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex1);
}
}
void critical_section(int thread_num, int i)
{
printf("Thread%d: %d\n", thread_num,i);
}
这个程序制造了一个死锁,主线程如果拿到了锁1,子线程拿到了锁2。那么主线程等待锁2,子线程等待锁1,就会造成死锁。所以我们可以去掉一个锁就行了。