进程线程互斥锁实验总结

进程实验:

/*  POSIX 下进程控制的实验程序*/
#include 
#include 
#include 
#include 
#include 
/* 允许建立的子进程个数最大值 */
#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个子进程要执行
        * proc_number = i;
        * do_something();
        * 父进程把子进程的id保存到pid[i] */
        child_pid=fork();
        proc_number = i;
        switch(child_pid)
        {
            case 0:do_something();   exit(0);
            case -1:perror("error"); exit(-1);
            default:pid[i]=child_pid;
        }
    }
    /* 让用户选择杀死进程,数字表示杀死该进程,q退出 */
    while ((ch = getchar()) != 'q')  
    {
        if (isdigit(ch))
        {
            /*  填写代码,向pid[ch-'0']发信号SIGTERM,杀死该子进程 */
            kill(pid[ch-'0'],SIGTERM);
        }
    }
    /* 在这里填写代码,杀死本组的所有进程 */
    kill(0,SIGTERM);
    return;
}
void do_something()
{
    for(;;)
    {  
        printf("This is process No.%d and its pid is %d\n",proc_number,  getpid());
        sleep(SLEEP_INTERVAL); // 主动阻塞两秒钟
    }
}

kill()函数用于删除执行中的程序或者任务。调用格式为: kill(int PID, int IID);
其中:PID是要被杀死的进程号,IID为向将被杀死的进程发送的中断号。

实验过程:

  先猜想一下这个程序的运行结果。假如运行“./process 20”,输出会是什么样?然后按照注释里的要求把代码补充完整,运行程序。开另一个终端窗口,运行“ps aux|grep process”命令,看看process 究竟启动了多少个进程。回到程序执行窗口,按“数字键+回车”尝试杀掉一两个进程,再到另一个窗口看进程状况。按q退出程序再看进程情况。

  假如运行”./process 20”,将会产生10个进程;另一个终端运行”ps aux|grep process”命令,看到启动了11个进程,其中1个为主进程,其他为子进程;按”数字键+回车”会发现杀死了对应数字的进程,切换到另一个终端下查看可以看到刚才杀死的进程在他的后面会出现<defunt>标志,表示该进程已经被杀死了。

回答下列问题:

  1. 你最初认为运行结果会怎么样?

      最初认为,如果./process n的n小于等于10,会有n个进程产生,大于10会有10个进程产生,但是输出顺序可能不完全按照proc_number的序号

  2. 实际的结果什么样?有什么特点?试对产生该现象的原因进行分析。

      实际结果确实如此,因为进程执行的顺序取决于调度算法,所以不完全按照顺序循环输出

  3. proc_number这个全局变量在各个子进程里的值相同吗?为什么?

      不相同,因为全局变量是共享资源,它记录的是子进程的序号,子进程在创建时,该变量进行了更新,子进程复制了父进程的数据段并存入自己的数据段中,所以在各个子进程里的值不同

  4. kill命令在程序中使用了几次?每次的作用是什么?执行后的现象是什么?

      一种是”数字键+回车”杀死指定进程,按几次就使用几次,被杀死的进程不会再出现在循环中;一种是”q”杀死所有进程,所有进程终止

  5. 使用kill命令可以在进程的外部杀死进程。进程怎样能主动退出?这两种退出方式哪种更好一些?

      可以调用exit函数或使用return来主动退出。我认为主动退出好,因为可以使进程直接结束,子进程不会进入僵死状态,也不会发生被动退出出现异常的情况


线程实验:

/*  POSIX 下线程控制的实验程序改进版 */
#include 
#include 
#include 
#include 
#include 

#define MAX_THREAD 3 /* 线程的个数 */
unsigned long long main_counter, counter[MAX_THREAD];
/* unsigned long  long是比long还长的整数 */

//改进static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void* thread_worker(void*);
int main(int argc,char* argv[])
{     
    int i, rtn, ch;     
    pthread_t pthread_id[MAX_THREAD] = {0}; /* 存放线程id*/
    for (i=0; i/* 在这里填写代码,用pthread_create建一个普通的线程,线程id存入pthread_id[i],线程执行函数是thread_worker,并i作为参数传递给线程 */
        pthread_create(&pthread_id[i],NULL,thread_worker,(void *)&i);
        //改进pthread_create(&pthread_id[i],NULL,thread_worker,(void *)i);
    }    
    do
    {
        //改进pthread_mutex_lock(&mutex);
        /* 用户按一次回车执行下面的循环体一次。按q退出 */          
        unsigned long long sum = 0;    
        /* 求所有线程的counter的和 */
        for (i=0; i/* 求所有counter的和 */         
            sum += counter[i];             
            printf("%llu ", counter[i]);        
        }
        printf("%llu/%llu", main_counter, sum);
        //改进pthread_mutex_unlock(&mutex);
    }while ((ch = getchar()) != 'q');

    //改进pthread_mutex_destroy(&mutex);
    return 0;
}
void* thread_worker(void* p)
{   
    int thread_num;  
    /* 在这里填写代码,把main中的i的值传递给thread_num */
    thread_num=*(int *)p;
    //改进thread_num=(int)p;
    for(;;)
    { 
        /* 无限循环 */
        //改进pthread_mutex_lock(&mutex);    
        counter[thread_num]++; /* 本线程的counter加一 */
        main_counter++; /* 主counter 加一 */   
        //改进pthread_mutex_unlock(&mutex);
    }
}

pthread_create函数说明:
extern int pthread_create ((pthread_t __thread, __const pthread_attr_t *__attr,void (__start_routine) (void ), void *__arg));
 第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数.

实验过程:

  按照注释里的要求把代码补充完整,正确编译程序后,先预计一下这个程序的运行结果。具体的结果会是什么样?运行程序。开另一个终端窗口,运行“ps aux”命令,看看thread 的运行情况,注意查看thread 的CPU 占用率,并记录下这个结果。

回答下列问题:

  1. 你最初认为前三列数会相等吗?最后一列斜杠两边的数字是相等,还是大于或者小于关系?

      最初不确定前三列的数,最后一列猜测是等于

  2. 最后的结果如你所料吗?有什么特点?对原因进行分析。

      结果是前三个数字,有两个或三个都会是0,因为创建线程时第四个参数传的是i的地址,而线程又是共享地址空间,在传地址的过程中,赋值之前有可能i++已经执行,所以可能会出现多个0

      最后一列是小于的关系,理论上来说,main_counter 是直接计算总的循环次数,counter[i]是计算第i号线程循环的次数,sum是3个线程各自循环次数的总和,所以,理论上main_counter和sum值应该是相等的,因为都是在计算总循环次数。但并发运行的程序中,可能当线程1还没完成加1操作的时候,此时,线程2也开始执行main_counter++,但是线程2看到的main_counter还是0,所以线程2完成了加1操作后,main_counter还是1,虽然两个线程各执行了一次加1操作,但是其实最终main_counter只加了1次,这就使得全局变量main_counter小于counter[i]

  3. thread 的CPU 占用率是多少?为什么会这样?

      298%,因为三个线程都是死循环,导致CPU占用率很高

  4. thread_worker()内是死循环,它是怎么退出的?你认为这样退出好吗?

      在main函数中设置的输入q时循环退出,return结束整个进程,我认为这样退出不好,因为临界资源为一个线程独占,线程终止而不释放临界资源可能会造成死锁

改进:在传参时,利用强制类型转换,直接传i的值,而不是传地址

改进:利用互斥锁,让main_counter++和counter[i]++保持同步更新,这两条语句中间不能被打断;并且求和操作完成后,不能再让 main_counter增加


互斥实验:

/* POSIX 下线程死锁的演示程序 */
#include 
#include 
#include 
#include 
#include 

#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; i1, 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//改进pthread_mutex_lock(&mutex1);
        pthread_mutex_lock(&mutex1);
        //改进pthread_mutex_lock(&mutex2);
        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);
}

实验一:

  找到thread.c 的代码临界区,用临界区解决main_counter 与sum 不同步的问题。

  改进在上题注释中~

实验二:

  仔细阅读程序,编译程序后,先预计一下这个程序的运行结果。运行程序。若程序没有响应,按ctrl+c 中断程序运行,然后再重新运行,如此反复若干次,记录下每次的运行结果。若产生了死锁,请修改程序,使其不会死锁。

回答下列问题

  1. 你预想deadlock.c 的运行结果会如何?

      运行中可能会卡死

  2. deadlock.c 的实际运行结果如何?多次运行每次的现象都一样吗?为什么会这样?

      实际开始几次运行正常,多次运行之后程序终止了,多次运行都是会终止.因为出现了死锁的现象,主线程申请mutex1资源,而子线程申请mutex2资源,此时主线程继续申请mutex2资源,子线程来申请mutex1资源,而mutex2资源还未被子线程释放,主线程无法申请到,同样的,mutex1资源未被主线程释放则子线程也无法申请到,此时便处于无限循环等待,造成死锁

你可能感兴趣的:(暑假留校周总结)