本文主要介绍如何通过 pthread 库进行多线程编程,并通过以下例子进行说明。

  • 基于莱布尼兹级数计算 \(\pi\) .
  • 多线程归并排序

参考文章:

API 介绍

pthread_create

作用:新建一个线程。

函数原型:

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

参数解析:

  • pthread_t *thread 用于缓存新线程的 pid .
  • const pthread_attr_t *attr 制定新线程的 attr ,如果为 NULL,那么将使用默认的 attr
  • start_routine 是新线程即将进入的执行函数。
  • arg 向新线程传递的某些参数,一般封装为结构体传入。

线程的中止可以通过以下方式:

  • 调用 pthread_exit(void *retval) , 其中 retval 可以通过 pthread_join 获得。
  • start_routine 函数直接 return .
  • 该线程被取消 (See pthread_cancel) .
  • 线程所属的进程调用了 exit , 或者该进程的 main 函数中执行了 return .

pthread_join

等待某个线程结束。

函数原型:

int pthread_join(pthread_t thread, void **retval);

参数解析:

  • thread 是某个线程的 pid .

  • retval 用于获取线程 start_routine 的返回值 .

基本用法请看下面的「双线程计算 \(\pi\)」,该例子同时能够回答为什么 retvalvoid** 类型而不是 void * 类型。

pthread_attr_t

pthread_attr_t 的定义如下:

struct __pthread_attr
{
struct sched_param __schedparam;
void *__stackaddr;
size_t __stacksize;
size_t __guardsize;
enum __pthread_detachstate __detachstate;
enum __pthread_inheritsched __inheritsched;
enum __pthread_contentionscope __contentionscope;
int __schedpolicy;
};

与之相关的 API,请看:

man pthread_attr

Examples

创建线程

下面是一个简单的多线程例子,用于演示 pthread_createpthread_join 的基本用法。

该例子创建 4 个线程,通过 order[i] 分别标号,线程的工作内容是输出本线程的标号。

所涵盖的知识点:

  • 如何创建线程
  • 如何向线程传递参数:通过对 void *arg 进行强制类型转换实现。
  • pthread_join 的作用:如果去掉 pthread_join 调用,那么程序很可能是没有输出的。因为在进入各个线程的 worker 函数时,main 函数已经结束,这时候所有线程都被强制终止。
#include <stdio.h>
#include <pthread.h>
const int N = 4;
void* worker(void *arg)
{
int *pid = (int *)arg;
printf("%d ", *pid);
return NULL;
}
int main()
{
pthread_t pid[N] = {0};
const int order[] = {0, 1, 2, 3};
int i = 0;
for (; i < N; i++)
pthread_create(&pid[i], NULL, worker, (void *)&order[i]);
for (i = 0; i < N; i++)
pthread_join(pid[i], NULL);
return 0;
}

双线程计算 π

要求:

  • 基于莱布尼兹级数:1 - 1/3 + 1/5 - 1/7 + 1/9 - ... = PI/4
  • 使用主线程 + 辅助线程的方式

涵盖知识点:

  • 如何向不同的线程传递不同参数
  • 如何获取线程的结果

代码实现:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
const int N = 1e8;
typedef struct { int start, end; } param_t;
typedef struct { double value; } result_t;
void *worker(void *arg)
{
param_t *param = (param_t *)arg;
result_t *res = (result_t *)malloc(sizeof(result_t));
int i = param->start;
for (; i <= param->end; i++)
{
if (i % 2) res->value += 1.0 / (2 * i - 1);
else res->value -= 1.0 / (2 * i - 1);
}
return res;
} double master(void *arg)
{
double res = 0.0;
param_t *param = (param_t *)arg;
int i = param->start;
for (; i <= param->end; i++)
{
if (i % 2) res += 1.0 / (2 * i - 1);
else res -= 1.0 / (2 * i - 1);
}
return res;
} int main()
{
pthread_t tid = 0;
param_t p1 = {1, N / 2}, p2 = {N / 2 + 1, N};
pthread_create(&tid, NULL, worker, &p2);
double val = master(&p1);
result_t *res = NULL;
pthread_join(tid, (void **)&res);
printf("PI = %f\n", 4 * (val + res->value));
free(res);
}

多线程计算 π

要求:

  • 适应 N 核心的 CPU
  • 不能使用全局变量,必须通过传递参数与 join 获取返回值实现

代码实现:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
const int N = 1e3;
const int NR_CPU = 8;
typedef struct { int start, end; } param_t;
typedef struct { double value; } result_t;
void *worker(void *arg)
{
param_t *p = (param_t *)arg;
int i = p->start;
result_t *res = malloc(sizeof(result_t));
for (; i < p->end; i++)
{
if (i % 2) res->value += 1.0 / (2 * i - 1);
else res->value -= 1.0 / (2 * i - 1);
}
return res;
} int main()
{
param_t params[NR_CPU];
pthread_t pids[NR_CPU] = {0};
const int step = N / NR_CPU;
int i = 0;
for (; i < NR_CPU; i++)
{
params[i].start = i * step + 1;
params[i].end = params[i].start + step;
}
params[NR_CPU - 1].end = N;
for (i = 0; i < NR_CPU; i++) pthread_create(&pids[i], NULL, worker, &params[i]);
result_t *res = NULL;
double pi = 0.0;
for (i = 0; i < NR_CPU; i++)
{
pthread_join(pids[i], (void **)&res);
pi += res->value;
if (res) free(res), res = NULL;
}
pi *= 4;
printf("PI = %f\n", pi);
return 0;
}

多线程归并排序

要求:

  • 把数组分为若干个区间,每个区间单独通过一个线程排序
  • 最后所有区间
#include <assert.h>
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
const int N = 1e6;
const int NR_CPU = 4;
typedef struct
{
int *nums;
int start, end;
} param_t;
int check(const int *nums, int len)
{
int i = 1;
for (; i < len; i++)
if (nums[i] < nums[i - 1])
return 0;
return 1;
}
int cmp(const void *a, const void *b) { return (*(int *)a) - (*(int *)b); }
void *worker(void *arg)
{
param_t *p = (param_t *)arg;
int *start = p->nums + p->start;
int n = p->end - p->start;
qsort(start, n, sizeof(int), cmp);
return NULL;
}
// merge [start, mid) and [mid, end)
void merge(const int *nums, int start, int mid, int end)
{
int *p = malloc(sizeof(int) * (end - start));
const int *p1 = nums + start, *p2 = nums + mid;
int len1 = mid - start, len2 = end - mid;
int idx = 0, i = 0, j = 0;
while (i < len1 && j < len2)
{
if (p1[i] < p2[j]) p[idx++] = p1[i++];
else p[idx++] = p2[j++];
}
while (i < len1) p[idx++] = p1[i++];
while (j < len2) p[idx++] = p2[j++];
memcpy((void *)(nums + start), (void *)p, sizeof(int) * idx);
}
int main()
{
srand(time(NULL));
int nums[N] = {0};
int i = 0;
for (; i < N; i++) nums[i] = random() % N; param_t params[NR_CPU];
int step = N / NR_CPU;
for (i = 0; i < NR_CPU; i++)
{
params[i].nums = nums;
params[i].start = i * step;
params[i].end = params[i].start + step;
}
params[NR_CPU - 1].end = N;
pthread_t pids[NR_CPU] = {0};
for (i = 0; i < NR_CPU; i++) pthread_create(&pids[i], NULL, worker, &params[i]);
for (i = 0; i < NR_CPU; i++) pthread_join(pids[i], NULL);
while (step < N)
{
int start = 0;
while (start < N)
{
int mid = start + step;
int end = mid + step;
if (mid > N) mid = N;
if (end > N) end = N;
merge(nums, start, mid, end);
start = end;
}
step *= 2;
}
assert(check(nums, N));
}

pthread 多线程基础的更多相关文章

  1. clone的fork与pthread_create创建线程有何不同&pthread多线程编程的学习小结(转)

    进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合,这些资源在Linux中被抽 象成各种数据对象:进程控制块.虚存空间.文件系统,文件I/O.信号处理函数.所以创建一个进程的 过程就是这 ...

  2. Java基础知识笔记(四:多线程基础及生命周期)

    一.多线程基础 编写线程程序主要是构造线程类.构造线程类的方式主要有两种,一种是通过构造类java.lang.Thread的子类,另一种是通过构造方法实现接口java.lang.Runnable的类. ...

  3. Java多线程干货系列—(一)Java多线程基础

    前言 多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域,所以学好多线程并发编程对我们来说极其重要,下面跟我一起开启本次的学习之旅吧. 正文 线程与进程 1 线程:进程中负责程序执行的 ...

  4. JAVASE02-Unit010: 多线程基础 、 TCP通信

    多线程基础 . TCP通信 * 当一个方法被synchronized修饰后,那么 * 该方法称为同步方法,即:多个线程不能同时 * 进入到方法内部执行. package day10; /** * 当多 ...

  5. JAVASE02-Unit09: 多线程基础

    Unit09: 多线程基础 * 线程 * 线程用于并发执行多个任务.感官上像是"同时"执行 *  * 创建线程有两种方式. * 方式一: * 继承线程并重写run方法来定义线程要执 ...

  6. java多线程基础

    多线程基础 读书练习照猫画虎 package Threadtest; import java.util.Date; import java.util.concurrent.ArrayBlockingQ ...

  7. C#编程总结(二)多线程基础

    C#编程总结(二)多线程基础 无论您是为具有单个处理器的计算机还是为具有多个处理器的计算机进行开发,您都希望应用程序为用户提供最好的响应性能,即使应用程序当前正在完成其他工作.要使应用程序能够快速响应 ...

  8. swift开发多线程篇 - 多线程基础

    swift开发多线程篇 - 多线程基础 iOS 的三种多线程技术 (1)NSThread  使用NSThread对象建立一个线程非常方便 但是!要使用NSThread管理多个线程非常困难,不推荐使用 ...

  9. Java多线程干货系列(1):Java多线程基础

    原文出处: 嘟嘟MD 前言 多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域,所以学好多线程并发编程对我们来说极其重要,下面跟我一起开启本次的学习之旅吧. 正文 线程与进程 1 线程 ...

随机推荐

  1. CSGO 服务端扩展插件开发记录之"DropClientReason"(1)

    最近开始接触到了CSGO这款游戏,还是老套路,就是想千方百计的从里面增添新的游戏功能,当然刚开始想做到游刃有余是有点困难, 跟之前做CS1.6的第三方开发一样,都得自己慢慢的摸索过来,纵然CSGO所使 ...

  2. tomcat加载失败

    tomcat启动加载信息如下: Connected to server [2017-10-16 09:02:28,149] Artifact basic-admin:war exploded: Art ...

  3. 2017-18-2 B2B实验+扩充

     B2B模拟实验 [实验目的] 1.掌握B2B中供应商的供求信息发布.阿里商铺开设和订单交易等过程. 2.掌握B2B中采购商的采购信息的发布.交易洽谈.网上支付和收货等过程. [实验条件] 1.个人计 ...

  4. Python爬虫框架--Scrapy安装以及简单实用

    scrapy框架 框架 ​ -具有很多功能且具有很强通用性的一个项目模板 环境安装: Linux: ​        pip3 install scrapy ​ ​ ​  Windows: ​     ...

  5. k8s node上查看节点

    node执行 mkdir -p /root/.kube master执行 scp admin.conf node1:/root/.kube/config

  6. spring boot:使用redis cluster集群作为分布式session(redis 6.0.5/spring boot 2.3.1)

    一,为什么要使用分布式session? HpptSession默认使用内存来管理Session,如果将应用横向扩展将会出现Session共享问题, 所以我们在创建web集群时,把session保存到r ...

  7. XSS攻击 js 脚本注入

    原文地址:http://www.cnblogs.com/robot/archive/2009/04/15/1436107.html 1.不要相信Request.QueryString: 相信在asp时 ...

  8. Redis实现缓存与分布式锁

    缓存与分布式锁 哪些数据适合放入缓存 即时性.数据一致性要求不高的 访问量大且更新频率不高的数据 选择redis做为缓存中间件 <dependency> <groupId>or ...

  9. docker gitlab搭建

    1,官方查找gitlab docker镜像 2,pull镜像 3,run docker run -d -p 443:443 -p 10080:80 -p 22:22 --name gitlab --r ...

  10. 强网杯web之假的反序列化漏洞

    说明 打强网杯的时候一直在写论文, 做林逸师傅的培训题目. 现在得空,还是看了一部分的题目和wp. 源码 源码一共三部分, 这里只写下我知识盲区的一部分,作为自己的记录. <?php highl ...