1. 前言
本次来写一篇关于C++多线程的基本使用。前面有一篇是互斥锁的入门,学了两天,做一下总结。
2. 多线程
(1) 创建
多线程的表示pthread_t:
/* Thread identifiers. The structure of the attribute type is not
exposed on purpose. */
typedef unsigned long int pthread_t;
在源码中,pthread_t实际上就只是一个无符号的long int变量,用于线程的唯一标识符。
下面是pthread_create的原型:
/* Create a new thread, starting with execution of START-ROUTINE
getting passed ARG. Creation attributed come from ATTR. The new
handle is stored in *NEWTHREAD. */
extern int pthread_create (pthread_t *__restrict __newthread,
const pthread_attr_t *__restrict __attr,
void *(*__start_routine) (void *),
void *__restrict __arg) __THROWNL __nonnull ((1, 3));
参数介绍:
__newthread表示线程的唯一标识符
__attr表示线程的属性,之后会介绍几个小方面的
__*(*__start_rountine)(void *)这个实际上就是一个函数的指针
__arg表示函数的参数,需要将其转化成void*指针才可以传值
线程创建之后如果不进行其他的操作,那么线程会在主线程结束的时候就结束,主线程不会等到所有线程结束后才结束,例如下面的代码:
#include
#include
#include
#include
#include
#include
using namespace std;
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t count_nonzero;
unsigned count;
void* decrement_count(void* data)
{
pthread_mutex_lock(&mut);
while(count == 0)
{
printf("1\n");
pthread_cond_wait(&count_nonzero,&mut);
}
count = count -1;
printf("Dec\n");
pthread_mutex_unlock(&mut);
}
void* increment_count(void* data)
{
pthread_mutex_lock(&mut);
if(count == 0)
pthread_cond_signal(&count_nonzero);
count = count + 1;
printf("inc\n");
pthread_mutex_unlock(&mut);
}
int main()
{
count = 0;
pthread_t tid[2];
pthread_create(&tid[0],NULL,decrement_count,NULL);
pthread_create(&tid[1],NULL,increment_count,NULL);
printf("main\n");
}
其运行结果可以为下面的一种:
第一种
1
main
第二种
inc
main
第三种
1
inc
main
...
当然后面那些需要你的线程抢到主线程的资源,否则你会发现永远是第一种情况。
第一种是什么情况呢?
很简单,主线程创建了新的线程,线程的运行顺序是没有保证的,这个时候第一个线程就先运行,但是又发现count = 0,线程就把锁释放。然后回到主线程,主线程又因为在main之后就结束了,使得这两个线程根本就没有时间执行,导致输出以这种结果。
(2) 线程的属性
属性对象主要包括是否绑定,是否分离,堆栈地址,堆栈大小,优先级等。
分离属性(detachstate):
分离:线程自己运行结束,则马上释放资源。
不分离:如果加入pthread_join中,则需要等待pthread_join执行完毕之后才算结束,并且释放资源。
所对应的宏定义为:
分离:PTHREAD_CREATE_DETACHED
不分离:PTHREAD_CREATE_JOINABLE
设置函数为:
/* Set detach state attribute. */
extern int pthread_attr_setdetachstate (pthread_attr_t *__attr,
int __detachstate)
__THROW __nonnull ((1));
注意,如果设置线程为分离的时候,线程的可能回现在pthread_create函数之前已经完成,导致返回错误的线程标识符。可以添加pthread_cond_timewait()来让线程等待一段时间,不能使用wait(),这个会使得整个进程都进入等待,并不能解决问题。
优先级属性(SCHED_PARAM):
就是线程执行的优先级,优先级高的先执行,当然,这在并发编程中也是没有保证的。
设置函数:
/* Return in *PARAM the scheduling parameters of *ATTR. */
extern int pthread_attr_getschedparam (const pthread_attr_t *__restrict __attr,
struct sched_param *__restrict __param)
__THROW __nonnull ((1, 2));
/* Set scheduling parameters (priority, etc) in *ATTR according to PARAM. */
extern int pthread_attr_setschedparam (pthread_attr_t *__restrict __attr,
const struct sched_param *__restrict
__param) __THROW __nonnull ((1, 2));
设置样例:
pthread_t tid[2];
pthread_attr_t attr;
sched_param param;
pthread_attr_init(&attr);
pthread_attr_getschedparam(&attr,¶m);
param.__sched_priority = 20;
pthread_attr_setschedparam(&attr,¶m);
pthread_create(&tid[0],&attr,decrement_count,NULL);
(3) pthread_join/pthread_exit
/* Make calling thread wait for termination of the thread TH. The
exit status of the thread is stored in *THREAD_RETURN, if THREAD_RETURN
is not NULL.
This function is a cancellation point and therefore not marked with
__THROW. */
extern int pthread_join (pthread_t __th, void **__thread_return);
pthread_join:可以将不分离的线程连接起来,直到这个函数调用结束的时候,线程才释放资源。这样也就可以解决前面的出现那种主线程结束了,线程没有执行完成就结束的情况。当然,这个会阻塞当前线程,直到所有的线程结束为止。
/* Terminate calling thread.
The registered cleanup handlers are called via exception handling
so we cannot mark this function with __THROW.*/
extern void pthread_exit (void *__retval) __attribute__ ((__noreturn__));
pthread_exit退出线程。这个后面的参数是返回值,返回值可以在__thread_return中。
下面是之前的代码:
#include
#include
#include
#include
#include
#include
using namespace std;
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t count_nonzero;
unsigned count;
void* decrement_count(void* data)
{
pthread_mutex_lock(&mut);
while(count == 0)
{
printf("1\n");
pthread_cond_wait(&count_nonzero,&mut);
}
count = count -1;
printf("Dec\n");
pthread_mutex_unlock(&mut);
pthread_exit(NULL);
}
void* increment_count(void* data)
{
pthread_mutex_lock(&mut);
if(count == 0)
pthread_cond_signal(&count_nonzero);
count = count + 1;
printf("inc\n");
pthread_mutex_unlock(&mut);
pthread_exit(NULL);
}
int main()
{
count = 0;
pthread_t tid[2];
pthread_attr_t attr;
sched_param param;
pthread_attr_init(&attr);
pthread_attr_getschedparam(&attr,¶m);
param.__sched_priority = 20;
pthread_attr_setschedparam(&attr,¶m);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);
pthread_create(&tid[0],&attr,decrement_count,NULL);
pthread_create(&tid[1],&attr,increment_count,NULL);
pthread_join(tid[0],NULL);
pthread_join(tid[1],NULL);
printf("main\n");
}
运行结果为:
1
inc
Dec
main
到这里,线程的知识基本上就这些了。
3. C++11 thread类
C++11中新添加了一个线程的类,在
使用示例:
#include
#include
#include
#include
#include
#include
#include
using namespace std;
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t count_nonzero;
unsigned count;
void decrement_count()
{
pthread_mutex_lock(&mut);
while(count == 0)
{
printf("1\n");
pthread_cond_wait(&count_nonzero,&mut);
}
count = count -1;
printf("Dec\n");
pthread_mutex_unlock(&mut);
pthread_exit(NULL);
}
void increment_count()
{
pthread_mutex_lock(&mut);
if(count == 0)
pthread_cond_signal(&count_nonzero);
count = count + 1;
printf("inc\n");
pthread_mutex_unlock(&mut);
pthread_exit(NULL);
}
int main()
{
count = 0;
thread t[2];
t[0] = thread(decrement_count);
t[1] = thread(increment_count);
t[0].join();
t[1].join();
}
有没有觉得函数变得简单易懂,而且很符合我们的编程习惯了= =
这个需要在执行join的时候才会运行线程,如果在主线程结束之前,没有join,会输出错误。
而且这个join是不会保证执行的顺序的,也就是主线程和join的线程的执行顺序是没有保证的,join只是保证join的线程一定能够运行到结束。
参考链接:
https://blog.csdn.net/stone_overlooking/article/details/78520945
http://www.runoob.com/cplusplus/cpp-multithreading.html
https://blog.csdn.net/chenxun_2010/article/details/49785611