• Pthread并行编程总结

    • 1. pthread_create

      • pthread_create的效果
      • 一个简单的线程例子
    • 2. 线程数据共享
    • 3. Pthread “Hello world”
      • 3.1 一些准备
      • 3.2 pthread_join函数
      • 3.3 “Hello World”
    • 4. Pthread 其他基础 API
      • 4.1 pthread_exit( )
      • 4.2 pthread_cancal()
        • 一个取消线程执行的例子
    • 5.综合例:多个数组排序
      • 动态任务分配

Pthread并行编程总结

1. pthread_create

int pthread_create(pthread_t *, const pthread_attr_t *,void * (*)(void *),   void *)

调用例:

errcode = pthread_create(&thread_id, &thread_attribute,&thread_fun, &fun_arg);
  • thread_id 线程ID或句柄(用于停止线程等)
  • thread_attribute 各种属性,空指针表示标准默认属性值
  • thread_fun 要运行的函数(参数和返回值类型都是void*)
  • fun_arg 传递给thread_fun的参数
  • errorcode 若创建失败,返回非零值

pthread_create的效果

  • 主线程借助操作系统创建一个新线程
  • 线程执行一个特定函数thread_fun
  • 所有创建的线程执行相同的函数,表示线程的计算任务分解
  • 对于程序中不同线程执行不同任务的情况,可用创建线程时传递的参数区分线程的“id”以及其他线程的独特特性

一个简单的线程例子

int main()
{pthread_t threads[16];int tn;for(tn=0;tn<16;tn++){pthread_create(&threads[tn],NULL,ParFun,NULL);}for(tn=0;tn<16;tn++){pthread_join(threads[tn],NULL);}return 0;
}

这段代码创建了16个线程执行函数“ParFun”.

注意:创建线程的代价很高,因此ParFun应完成很多工作才值得付出这种代价

2. 线程数据共享

  • 全局变量都是共享的
  • 在堆中分配的对象可能是共享的(指针共享)
  • 栈中的变量是私有的:将其指针传递给其他线程可能导致问题
  • 常用共享方式:创建一个“线程数据”结构传递给所有线程,例如:
char *message = "Hello World!\n";     pthread_create( &thread1,NULL,(void*)&print_fun,(void*) message);

3. Pthread “Hello world”

3.1 一些准备

  • 线程数(threadcount)运行时设置,从命令行读取
  • 每个线程打印“Hello from thread <X> of <threadcount>”

3.2 pthread_join函数

int pthread_join(pthread_t *, void **value_ptr);

说明:

  • 作用:“挂起调用线程,直至目标线程结束,除非目标线程已结束。”
  • 第二个参数允许目标线程退出时返回信息给调用线程(通常是NULL)
  • 如发生错误返回非零值

3.3 “Hello World”

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>/* Global variable: accessible to all threads */
int thread_count;// 线程执行函数
void* Hello(void* rank); /* Thread function */int main(int argc, char* argv[]) {long thread; /* Use long in case of a 64-bit system */// 线程句柄pthread_t* thread_handles;/* Get number of threads from command line */thread_count = strtol(argv[1], NULL, 10);thread_handles = malloc(thread_count*sizeof(pthread_t));// 创建线程for (thread = 0; thread < thread_count; thread++)pthread_create(&thread_handles[thread], NULL, Hello, (void*) thread);printf("Hello from the main thread\n");// 等待线程结束for (thread = 0; thread < thread_count; thread++)pthread_join(thread_handles[thread], NULL);free(thread_handles);return 0;
} /* main */void* Hello(void* rank) {long my_rank = (long) rank;  /* Use long in case of 64-bit system */printf("Hello from thread %ld of %d\n", my_rank, thread_count);return NULL;
} /* Hello */

可能的输出结果:

Hello from thread 1 of 4
Hello from thread 3 of 4
Hello from thread 0 of 4
Hello from the main thread
Hello from thread 2 of 4

4. Pthread 其他基础 API

4.1 pthread_exit( )

void pthread_exit(void *value_ptr);

通过value_ptr返回结果给调用者

4.2 pthread_cancal()

int pthread_cancel(pthread_t thread);

取消线程thread执行

一个取消线程执行的例子
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>void *threadFunc(void *parm)
{while(1){fprintf(stdout, "I am the child thread.\n");// 检测线程是否处于取消状态,若是,在此处退出线程pthread_testcancel();sleep(1);}
}int main(int argc, char *argv[])
{void *status;pthread_t   thread;pthread_create(&thread, NULL, threadFunc, NULL);sleep(3);// 向线程发出取消信号pthread_cancel(thread);// 等待线程真的退出pthread_join(thread, &status);if (status == PTHREAD_CANCELED)fprintf(stdout, "The child thread has been canceled.\n");elsefprintf(stderr, "Unexpected thread status!\n");return 0;
}

运行结果:

I am the child thread.
I am the child thread.
I am the child thread.
I am the child thread.
The child thread has been canceled.

5.综合例:多个数组排序

  • 多个一维数组也可看作一个矩阵,对每行(一维数组)进行排序
  • 与矩阵与向量相乘有何差别?

​ 乘法并行采取数据划分,把数据分配给不同的进程/线程,

#include <iostream>
#include <algorithm>
#include <vector>
#include <time.h>
#include <immintrin.h>
#include <windows.h>
#include <pthread.h>using namespace std;typedef struct{int threadId;
} threadParm_t;const int ARR_NUM = 10000;
const int ARR_LEN = 10000;
const int THREAD_NUM = 4;
const int seg = ARR_NUM / THREAD_NUM;//数组数/线程数=每个线程的任务量vector<int> arr[ARR_NUM];
pthread_mutex_t mutex;
long long head, freq;        // timersvoid init(void)
{srand(unsigned(time(nullptr)));for (int i = 0; i < ARR_NUM; i++) {arr[i].resize(ARR_LEN);for (int j = 0; j < ARR_LEN; j++)arr[i][j] = rand();}
}void *arr_sort(void *parm)
{threadParm_t *p = (threadParm_t *) parm;int r = p->threadId;long long tail;// 每个线程的计算量// 每个线程负责连续n/4个数组的排序for (int i = r * seg; i < (r + 1) * seg; i++)sort(arr[i].begin(), arr[i].end());pthread_mutex_lock(&mutex);QueryPerformanceCounter((LARGE_INTEGER *)&tail);printf(“Thread %d: %lfms.\n", r, (tail - head) * 1000.0 / freq);pthread_mutex_unlock(&mutex);pthread_exit(nullptr);
}int main(int argc, char *argv[])
{QueryPerformanceFrequency((LARGE_INTEGER *)&freq);init();mutex = PTHREAD_MUTEX_INITIALIZER;pthread_t thread[THREAD_NUM];threadParm_t threadParm[THREAD_NUM];QueryPerformanceCounter((LARGE_INTEGER *)&head);for (int i = 0; i < THREAD_NUM; i++){threadParm[i].threadId = i;pthread_create(&thread[i], nullptr, arr_sort, (void *)&threadParm[i]);}for (int i = 0; i < THREAD_NUM; i++){pthread_join(thread[i], nullptr);}pthread_mutex_destroy(&mutex);
}

结果:

//单线程
Thread 0: 7581.931894ms.
//4线程
Thread 3: 1942.302817ms.
Thread 2: 1948.374916ms.
Thread 0: 1955.479851ms.
Thread 1: 1969.761978ms.

虽然数据完全随机,但每个线程数据分布是一致的,因此达到了负载均衡。

如果生成的是不是同一分布的随机数,结果就没有这么好。如下:

void init_2(void)
{int ratio;srand(unsigned(time(nullptr)));for (int i = 0; i < ARR_NUM; i++) {arr[i].resize(ARR_LEN);if (i < seg) ratio = 0;else if (i < seg * 2) ratio = 32;else if (i < seg * 3) ratio = 64;else ratio = 128;if ((rand() & 127) < ratio)for (int j = 0; j < ARR_LEN; j++)arr[i][j] = ARR_LEN - j;elsefor (int j = 0; j < ARR_LEN; j++)arr[i][j] = j;}
}

前1/4:完全升序

第二段:1/4逆序,3/4升序

第三段:1/2逆序,1/2升序

第四段:完全逆序

块划分负载不均!

运行时间:

//单线程
Thread 0: 1643.106837ms.
// 4线程
Thread 0: 428.869616ms.
Thread 1: 486.402280ms.
Thread 2: 530.073299ms.
Thread 3: 643.510582ms

并行代价是643.5*4!

动态任务分配

int next_arr = 0;
pthread_mutex_t  mutex_task;
void *arr_sort_fine(void *parm)
{threadParm_t *p = (threadParm_t *) parm;int r = p->threadId;int task = 0;long long tail;while (1) {// 获取任务(串行)pthread_mutex_lock(&mutex_task);task = next_arr++;// 动态任务划分pthread_mutex_unlock(&mutex_task);// 如果任务池为空,停止if (task >= ARR_NUM) break;stable_sort(arr[task].begin(), arr[task].end());}pthread_mutex_lock(&mutex);QueryPerformanceCounter((LARGE_INTEGER *)&tail);printf("Thread %d: %lfms.\n", r, (tail - head) * 1000.0 / freq);pthread_mutex_unlock(&mutex);pthread_exit(nullptr);
}

结果:

Thread 0: 549.246907ms.
Thread 3: 552.934092ms.
Thread 2: 556.541263ms.
Thread 1: 559.427082ms

粗粒度动态划分——每次分配50行 :

Thread 0: 520.849620ms.
Thread 1: 524.470671ms.
Thread 3: 527.458957ms.
Thread 2: 530.890995ms.

细粒度任务划分会负载均衡,但是同步开销也很大,至于怎样划分粒度合适,还需实验。

Pthread并行编程总结相关推荐

  1. 并行编程中的“锁”难题

    在并行程序中,锁的使用会主要会引发两类难题:一类是诸如死锁.活锁等引起的多线程Bug:另一类是由锁竞争引起的性能瓶颈.本文将介绍并行编程中因为锁引发的这两类难题及其解决方案. 1. 用锁来防止数据竞跑 ...

  2. 并行编程1——什么是并行程序?

    首先了解几个概念 1. 串行 最基本的程序执行方式,串行程序的整个运行时,只有一个调用栈和一个运行时上下文. 2. 并发 多线程出现后比较常见的程序执行方式,多线程程序运行时,会有多个运行时上下文和对 ...

  3. 如何进行并行编程:从并行矩阵运算开始

    矩阵计算 矩阵计算问题有很多种类型,例如: 求解线性代数方程组 Ax = b 线性最小二乘问题 given b in R^m, for x in R^n,minimize ||Ax - b||^2 矩 ...

  4. C++ 并行编程(thread)

    C++ 并行编程 1. 进程和线程 1.1 常规解释 1.2 总结 1.3 具体理解 1.4 为什么使用多线程 1.5 进程和线程的区别 2. 并发与并行 2.1 多进程并发 2.2 多线程并发 3. ...

  5. OpenMP并行编程

    1.总览   OpenMP(Open Multi-Processing)是一种用于共享内存并行系统的多线程程序设计方案,支持的编程语言包括C.C++和Fortran.OpenMP提供了对并行算法的高层 ...

  6. 多核时代,并行编程为何“臭名昭著”?

    作者 | Yan Gu 来源 | 转载自知乎用户Yan Gu [导读]随着计算机技术的发展,毫无疑问现代计算机的处理速度和计算能力也越来越强.然而细心的同学们可能早已注意到,从2005年起,单核的 C ...

  7. linux c 并行编程从入门到精通,VISUAL STUDIO 2010并行编程从入门到精通(微软技术丛书)...

    摘要: <微软技术丛书:Visual Studio2010并行编程从入门到精通>循序渐进,步骤式动手练习迅速帮助读者掌握并行编程的基础知识. <微软技术丛书:Visual Studi ...

  8. 用Hadoop进行分布式并行编程

    程序实例与分析 Hadoop 是一个实现了MapReduce 计算模型的开源分布式并行编程框架,借助于Hadoop, 程序员可以轻松地编写分布式并行程序,将其运行于计算机集群上,完成海量数据的计算.在 ...

  9. Pthread多线程编程之查看Pthread版本的方法

    Pthread多线程编程之查看Pthread版本的方法: getconf GNU_LIBPTHREAD_VERSION [root@localhost Workspace]# getconf GNU_ ...

最新文章

  1. matlab 2014 破解使用
  2. DIV层跟随鼠标位置显示提示
  3. 北斗导航 | 卫星导航在动态监测中的应用(RTK)
  4. java classpath import package 机制 @Java的ClassPath, Package和Jar
  5. Redis分布式锁的实现原理看这篇就够了~
  6. burp基本的用法总结
  7. 事关Animation Tree的工作随笔(一)
  8. java将数组加上千分号_PHP实现对数字分隔加千分号的方法
  9. java 支付宝 验证签名失败,关于支付宝签名校验失败的问题排解
  10. origin2018使用说明
  11. 虚拟机安装ubuntu的相关经验总结及常见疑问
  12. 数据表为什么又叫透明表?
  13. 字节跳动 C++面经总结第四期
  14. 彻底删除微软拼音输入法这个讨厌的家伙
  15. SqlService基础一篇搞定(建库建表、插入数据、修改和删除数据、基础查询、条件查询、模糊查询、聚合函数、分组查询、多表查询)
  16. git上传代码报错:hint: Updates were rejected because a pushed branch tip is behind its remote hint: counter
  17. 前端:深拷贝的多种方法(超全详解)
  18. 新版Win10来了!丑哭了?
  19. 用通俗易懂的方式讲解:CatBoost 算法原理及案例
  20. 企业视频制作的多媒体软件

热门文章

  1. python画一个心形照片墙怎么摆_心形照片墙怎么摆
  2. Linux/Unix系统连接库使用 (zz)
  3. DotNetCore调用Https
  4. ubuntu 无法修改pdf的打开方式
  5. 女生应该选JAVA还是前端?
  6. python数值分析算例_只要一杯秋天的奶茶,就能学会Python数值分析(2)
  7. 度晓晓再战高考:百度“AI伙伴”助阵,人均学霸时代来了
  8. VMware中linux添加网卡eth1后找不到网卡的问题Linux重启网卡报错:Bringing up interface eth0:1......
  9. 插入排序(折半查找优化插入排序||希尔排序) _清风明月
  10. 第10章 - 文件操作