线程学习笔记

在线程这一章节里感觉比进程更然了,进程呢一般你创建了他,他就会继承父进程,然后单独再开辟一块空间。在自己的地盘上做操作,都不会影响其他进程。而线程就不一样了,如果你使用了全局变量。他由于调度问题,你胡乱改,出现各种意想不到结果。灰常坑。。。
一下总结几个代码,记录一些坑点。。。

  • 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,就会造成死锁。所以我们可以去掉一个锁就行了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值