美文网首页
C语言多线程基础入门

C语言多线程基础入门

作者: xuzhougeng | 来源:发表于2020-03-25 11:44 被阅读0次

    多线程的优势

    1. 线程创建更加快速
    2. 线程间切换更加快速
    3. 线程容易终止
    4. 线程间通讯更快速

    C语言的多线程可以通过gcc编译器中的pthread实现。

    案例1: hello world

    #include <stdio.h>
    #include <pthread.h>
    
    void *myfunc(void *args){
        printf("hello world!\n");
        return NULL;
    }
    
    
    int main(int argc, char const *argv[])
    {
        pthread_t pt; //定义线程ID
        pthread_create(&pt, NULL, myfunc, NULL); //创建线程
        pthread_join(pt, NULL); //等待线程结束
    
        return 0;
    }
    
    

    在上面的案例中,我们的main函数就是一个主线程,我们通过pthread_create创建新的线程。主线程可以将任务放在一个队列中,用线程ID控制每个工作线程处理哪些任务。

    因此我们需要学习核心函数pthread_create的用法,

    int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                              void *(*start_routine) (void *), void *arg);
    

    它需要四个参数作为输入:

    1. 线程ID的地址,可以通过pthread_t进行定义,
    2. 线程的属性,先不展开,直接用NULL
    3. 调用的函数
    4. 传入的参数,可以为NULL

    对于第三个参数,pthread_create要求该函数格式为void *函数名(void *args){}形式,函数的参数对应第四个参数。

    我们将上面的代码保存为example1.c,然后进行编译运行

    gcc -o example1 example1.c -lpthread
    ./example1
    

    案例2: 多线程的hell world

    上面代码中如果想要多个hello word, 最简单粗暴的方法就是通过手动复制的方法强行开多个线程,但是这样子就把线程给固定了,最好的方式是能够手动调整

    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    
    
    void *myfunc(void *args){
    
        printf("hello world!\n");
    
        return NULL;
    }
    
    
    int main(int argc, char const *argv[])
    {
        int n_threads = atoi(argv[1]);
        pthread_t pt[n_threads];
    
        for (int i = 0; i < n_threads; i++){
            pthread_create(&pt[i], NULL, myfunc, NULL);
        }
        for (int i = 0; i < n_threads; i++){
            pthread_join(pt[i], NULL);
        }
    
    
    
        return 0;
    }
    

    我们创建了一个存放不同线程ID的数组,通过一个for循环,创建多个线程运行,之后通过for循环等待线程结束。

    将上面的代码保存为example2.c,然后编译运行。

    gcc -o example2 example2.c -lpthread
    ./example2
    

    案例3: 数组分区间计算

    案例1和案例2,我们都没有传入额外的参数,输出结果也只是直接输出到屏幕。这个案例,我们会创建一个大小为5000的数组,通过多线程分区块计算,然后合并。

    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    
    #define ARRSIZE 5000
    //定义存放参数的结构体
    typedef struct{
        int start;
        int end;
        int sum;
        int *arr;
        pthread_t id;
    } ptInfo;
    //运行函数
    void *myfunc(void *args){
    
        ptInfo* info = (ptInfo *)args; //将void类型强制转换
        int sum = 0;
        for (int i = info->start; i < info->end; i++){
            sum += info->arr[i];
        }
    
        info->sum = sum; //返回sum
    
        return NULL;
    }
    
    
    int main(int argc, char const *argv[])
    {
        int n_threads = atoi(argv[1]);
        ptInfo pt[n_threads];
        int blocks = ARRSIZE / n_threads;
    
        //初始化数组
        int arr[ARRSIZE];
        for (int i = 0; i < ARRSIZE; i++){
            arr[i] = i;
        }
        //创建线程
        for (int i = 0, j=0; i < ARRSIZE; j++){
            pt[j].start = i;
            pt[j].end = i + blocks;
            pt[j].sum = 0;
            pt[j].arr = arr;
            fprintf(stderr, "running:%d-%d\n", pt[j].start, pt[j].end); //提示信息
            pthread_create(&pt[j].id, NULL, myfunc, &pt[j]);
            i += blocks;
        }
    
        int sum = 0;
        for (int i = 0; i < n_threads; i++ ){
            pthread_join(pt[i].id, NULL);
            sum += pt[i].sum;
        }
    
        printf("sum: %d\n", sum);
    
        return 0;
    }
    

    由于pthread_create只接受一个传入参数,但是我们要提供的参数不只两个,因此我们定义了一个结构体,结构体中存放数组内存地址,起始位置和终止位置,求和结果,线程ID信息。

    运行函数中,需要先将void *类型转换成我们定义的结构体指针类型,最后计算结果更新到结构体中sum中。

    接着我们写了一个循环,为每个线程分配处理范围,并创建线程。最后等待每个线程结束后,将计算结果保存到我们的sum中。

    最后,我们将其保存为example3.c, 然后编译运行

    gcc -o example3 example3.c -lpthread
    ./example3 10
    running:0-500
    running:500-1000
    running:1000-1500
    running:1500-2000
    running:2000-2500
    running:2500-3000
    running:3000-3500
    running:3500-4000
    running:4000-4500
    running:4500-5000
    sum: 1249750
    

    注意这个代码存在bug,提供的线程数必须能被5000整除,不然就数组最后部分可能不会被算到。

    以上几个案例只是简单介绍了C语言多线程的基本用法,处理数据也是相互独立,因此就不存在竞态条件(race condition), 也不需要引入互斥锁(mutex) ,也不涉及到假共享(false sharing)等高级知识点。对于这些知识点,可以参阅相关资料学习。

    相关文章

      网友评论

          本文标题:C语言多线程基础入门

          本文链接:https://www.haomeiwen.com/subject/hoetuhtx.html