讲到线程,Java的线程目前只知道从thread继承,并用start函数启动线程,稍后会多了解JAVA线程相关知识,补充到这一章中。
C++的线程在读硕期间用到过多次,下面首先总结一下:
1、C++线程
(1)MFC多线程
这种方式我没用过,所以这里只是提一下,主要在MFC中,一般用全局函数AfxBeginThread()来创建并初始化一个线程的运行,该函数有两种重载形式,分别用于创建工作者线程和用户界面线程。
(2)phtread
POSIX线程(POSIX threads),简称Pthreads,是线程的POSIX标准。该标准定义了创建和操纵线程的一整套API。在类Unix操作系统(Unix、Linux、Mac OS X等)中,都使用Pthreads作为操作系统的线程。Windows操作系统也有其移植版pthreads-win32。
利用phread编写的程序可以跨平台执行,Pthread的使用方式可以参考博主这篇文章:http://blog.csdn.net/feiyangtianyao/article/details/23368969
Pthread的简单使用
在一个要求使用多线程的项目中,一般使用Phread的情况下,下面几个函数即可完成任务:
pthread_t:线程ID
pthread_create():创建一个线程
pthread_exit():终止当前线程
pthread_cancel():中断另外一个线程的运行
pthread_join():阻塞当前的线程,直到另外一个线程运行结束
pthread_kill():向线程发送一个信号
pthread_mutex_init() 初始化互斥锁
pthread_mutex_destroy() 删除互斥锁
pthread_mutex_lock():占有互斥锁(阻塞操作)
pthread_mutex_unlock(): 释放互斥锁
pthread_self(): 查询线程自身线程标识号
pthread_equal(): 对两个线程的线程标识号进行比较
示列代码(VS 已卸,代码是拷贝的):
#include <iostream>
#include <pthread.h> //多线程相关操作头文件,可移植众多平台
using namespace std;
#define NUM_THREADS 5 //线程数
void* say_hello( void* args )
{
cout << "hello..." << endl;
} //函数返回的是函数指针,便于后面作为参数
int main()
{
pthread_t tids[NUM_THREADS]; //线程id
for( int i = 0; i < NUM_THREADS; ++i )
{
int ret = pthread_create( &tids[i], NULL, say_hello, NULL ); //参数:创建的线程id,线程参数,线程运行函数的起始地址,运行函数的参数
if( ret != 0 ) //创建线程成功返回0
{
cout << "pthread_create error:error_code=" << ret << endl;
}
}
pthread_exit( NULL ); //等待各个线程退出后,进程才结束,否则进程强制结束,线程处于未终止的状态
}
更多详细介绍可以参考转载的这篇文章:http://blog.csdn.net/feiyangtianyao/article/details/28625821
(3)OpenMP
OpenMP是一种简单的多线程工具,OpenMP的细粒度并行特性,使其常被用于高性能计算中。
OpenMP的使用简单,在windows下只需设置菜单栏->Project->Properties,弹出菜单里,点击 Configuration Properties->C/C++->Language->OpenMP Support,在下拉菜单里选择Yes。
在Linux下,利用命令gcc/g++ -fopenmp 即可。
如果你只想用OpenMP简单实现多线程的开发,那么以下命令即可实现:
#pragma omp parallel for;实现for循环的并行计算;
#pragma omp parallel sections,#pragma omp section;代码块的并行计算。
#pragma omp task 显式创建任务。
#pragma omp single 选中某个线程执行接下来的语句;
#pragma omp master选择0号线程执行接下来的语句。
#pragma omp barrier显式栅障,所有线程执行到此处等待。
omp_get_num_procs()得到CPU核心数;
omp_get_num_threads()得到OpenMP的线程数,OpenMP的线程数可以在程序中手动设置。
omp_get_wtime()得到当前时间,单位是s, 通常利用两个时间取程序的执行时间,这个时间是程序运行时间,不是总的CPU时间。
omp_init_lock(omp_lock *) 初始化互斥器
omp_destroy_lock(omp_lock *) 销毁互斥器
omp_set_lock(omp_lock *) 获得互斥器
omp_unset_lock(omp_lock *) 释放互斥器
omp_test_lock(omp_lock *) 试图获得互斥器,如果获得成功返回true,否则返回false
由于OpenMP设计的初衷是高性能并行计算,因此,锁操作的使用应当尽量避免,可以使用显式并行或者改造程序结构的方法避免锁的频繁使用。
OpenMP中还有其他需要条件关键字的使用,例如private,firstprivate,lastprivate等,这里不再详细说明。可以参考OpenMP的官方文档。
常常利用if(i==omp_get_thread_num())来控制单个线程的执行情况。
代码示列(拷贝别人的,这么简单的东西,就当熟悉一下):
#include <iostream>
#include <omp.h>
int main(){
int sum = 0;
int a[10] = {1,2,3,4,5,6,7,8,9,10};
int coreNum = omp_get_num_procs();//获得处理器个数
int* sumArray = new int[coreNum];//对应处理器个数,先生成一个数组
for (int i=0;i<coreNum;i++)//将数组各元素初始化为0
sumArray[i] = 0;
#pragma omp parallel for
for (int i=0;i<10;i++)
{
int k = omp_get_thread_num();//获得每个线程的ID
sumArray[k] = sumArray[k]+a[i];
}
for (int i = 0;i<coreNum;i++)
sum = sum + sumArray[i];
std::cout<<"sum: "<<sum<<std::endl;
return 0;
}
C++中也有其他多线程工具,比如Cilk,CUDA(GPU的线程)。
2、JAVA线程
JAVA多线程实现方式主要有三种:继承Thread类、实现Runnable接口和Executor框架。
http://blog.csdn.net/aboy123/article/details/38307539这篇文章讲的很清楚了,没必要献丑了,这里只做一个简介吧。
(1)继承Thread类实现多线程
继承Thread类的方法尽管被我列为一种多线程实现方式,但Thread本质上也是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法
(2)实现Runnable接口实现多线程
在我们刚接触的时候可能会迷糊继承Thread类和实现Runnable接口实现多线程,其实在接触后我们会发现这完全是两个不同的实现多线程,一个是多个线程分别完成自己的任务,一个是多个线程共同完成一个任务。
其实在实现一个任务用多个线程来做也可以用继承Thread类来实现只是比较麻烦,一般我们用实现Runnable接口来实现,简洁明了。
大多数情况下,如果只想重写 run() 方法,而不重写其他 Thread 方法,那么应使用 Runnable 接口。这很重要,因为除非程序员打算修改或增强类的基本行为,否则不应为该类(Thread)创建子类。
具体讲解参照:http://mars914.iteye.com/blog/1508429
(3)Executor框架
Executor框架是指java 5中引入的一系列并发库中与executor相关的一些功能类,其中包括线程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等。
并发编程的一种编程方式是把任务拆分为一些列的小任务,即Runnable,然后在提交给一个Executor执行,Executor.execute(Runnalbe) 。Executor在执行时使用内部的线程池完成操作。