c++多线程入门_c语言是单线程还是多线程

c++多线程入门_c语言是单线程还是多线程多线程的优势线程创建更加快速线程间切换更加快速线程容易终止线程间通讯更快速C语言的多线程可以通过gcc编译器中的pthread实现。案例1:helloworld#include<stdio.h>#include<pthread.h>void*myfunc(void*args){printf(“helloworl…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

多线程的优势

  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;
}

Jetbrains全家桶1年46,售后保障稳定

在上面的案例中,我们的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)等高级知识点。对于这些知识点,可以参阅相关资料学习。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/179379.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 使用vscode编写、运行Python程序[通俗易懂]

    使用vscode编写、运行Python程序[通俗易懂]最近准备推出一系列Python入门、Pytorch深度学习框架入门的文章,主要面向计算机视觉小白。为了给非计算机专业的读者、或者刚入门计算机视觉的读者打好基础,前几天特意写了一篇入门文章,介绍了计算机视觉中一些最基础的概念和名词:Hereisyuquedoccard,clickonthelinktoview:https://www.yuque.com/zhangkai-bazwc/gmeyak/ouxwcg今天主要和大家分享在vscode中编写、运行Python程序的方法,以

    2022年10月23日
  • Linux 的 history 命令使用大全

    Linux 的 history 命令使用大全history命令history命令:用于显示历史记录和执行过的指令命令。history命令读取历史命令文件中的目录到历史命令缓冲区和将历史命令缓冲区中的目录写入命令文件。该命令单独使用时,仅显示历

    2022年7月4日
  • Android Studio gardle 配置 ndk 指定 ABI: abiFilters 详解

    Android Studio gardle 配置 ndk 指定 ABI: abiFilters 详解一、ABI是什么ABI是ApplicationBinaryInterface的缩写。不同Android手机使用不同的CPU,因此支持不同的指令集。CPU与指令集的每种组合都有其自己的应用二进制界面(或ABI)。ABI可以非常精确地定义应用的机器代码在运行时如何与系统交互。您必须为应用要使用的每个CPU架构指定ABI。典型的ABI包含以下信息:机…

    2022年5月11日
  • python中for循环的用法-Python for循环及基础用法详解

    python中for循环的用法-Python for循环及基础用法详解Python中的循环语句有2种,分别是while循环和for循环,前面章节已经对while做了详细的讲解,本节给大家介绍for循环,它常用于遍历字符串、列表、元组、字典、集合等序列类型,逐个获取序列中的各个元素。for循环的语法格式如下:for迭代变量in字符串|列表|元组|字典|集合:代码块格式中,迭代变量用于存放从序列类型变量中读取出来的元素,所以一般不会在循环中…

    2022年8月12日
  • 如果我说熟悉SpringBoot 面试官会怎么问?

    如果我说熟悉SpringBoot 面试官会怎么问?SpringBoot因简化了Spring框架使用难度,极大地提高了Java企业级应用开发的效率,成为企业考核人才的重要标准之一。但随着现今互联网行业快速发展、企业业务不断深入,相应地对SpringBoot技术要求也愈来愈高。春节时期有一位打算金三银四面试的读者私信问我:如果我说熟悉SpringBoot面试官会怎么问?​可能不少朋友跟他一样,不清楚当下企业真实生产环境下对SpringBoot有哪些具体要求,需要掌握到什么程度。为此,结合这些年的面试经历及各大厂的职位要求,给

    2022年6月5日
  • 用 Javascript 生成二维码

    用 Javascript 生成二维码用Javascript生成二维码#javascript#Webdev的#节点#设计大家好????,这将是一篇很短的文章,我将展示如何为JavaScript中的任何内容生成二维码。显然,我不会从头开始实现所有内容,当我们在JavaScript中有大量有用的库时,为什么要这样做。我遇到了这个很棒的轻量级库,或者你可以说一个简单的脚本qrcodejs。它非常易于使用并且也很可靠。执行 下载此zip文件:qrcodejs 提取它。 现在您可以…

    2022年10月18日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号