本文主要介绍如何通过 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. MeteoInfoLab脚本示例:OMI Grid HDF数据

    OMI卫星格点数据的例子,全球臭氧柱总量分布.脚本程序: #Add data file folder = 'D:/Temp/hdf/' fns = 'OMI-Aura_L3-OMTO3e_2005m1 ...

  2. Mysql架构与内部模块-第三章

    前言 接上文,本篇文章专门简述Mysql存储引擎,内容繁多,如果你只需知道每种存储引擎的适用场景,可以直接查看本文最后列出的适用场景部分. 正文: Mysql存储引擎作为本系列文章中相对重要的一环,也 ...

  3. swoft 事件监听和触发 打印sql日志

    需求 打印出swoft的所有sql日志到控制台或者文件 只要打开listener 下面 Dbranlisten.php 里面最后一行注释即可,swoft已经帮我们实现好了 ____ _____ ___ ...

  4. C++ Primer第5版 第三章课后练习

    练习3.1 #include <iostream> using namespace std; int main() { int sum = 0, val = 50; while (val ...

  5. C# 面试前的准备_基础知识点的回顾_04

    1.Session和Cookie的使用区别 很容易回答的就是Session在服务器端,存储的数据可以较大容量,比如我们存一个Table,上千条数据. Cookie保存在客户端,安全系数低,不能放重要的 ...

  6. 想买保时捷的运维李先生学Java性能之 运行时数据区域

    前言 不知道自己不知道,不知道自己知道,知道自己不知道,知道自己知道,目前处于知道自己不知道这个阶段,很痛苦啊,干了4年了运维,是一个坎.越来越发觉想要走得远,还是得扎根底.   一.运行时数据区域 ...

  7. 对于72种常用css3的使用经验

    对于72种常用css3的使用经验 保存网站源码 目的 保证有足够的css和js文件源码 拿到当前网页的整体布局 本地化 创建web项目 将web项目创建出来 在项目中创建一个文件夹 将所有的js和cs ...

  8. js一些注意事项

    0.正则表达式,千万不能加引号 1.json对象的key必须用双引号,否则parse时可能出错: json对象不能直接存储时间对象,需要将时间对象加双引号转为字符串,存储,然后对表示时间的属性进行ne ...

  9. ECMAScript 6 入门 - 阮一峰

    body #home { width: 100%; max-width: 1368px } #inlineFrame { width: 100%; height: calc(100vh - 30px) ...

  10. Mybatis---04Mybatis配置文件浅析(二)

    本文参考:https://www.cnblogs.com/yulinfeng/p/5991170.html 1.typeHandlers:类型处理器,设置预处理语句(PreparedStatement ...