C++ 多线程

本文主要讲一下C++多线程

线程好处

·使用线程可以把占据长时间的程序中的任务放到后台去处理

·程序的运行速度可能加快

可以释放一些珍贵的资源如内存占用等等。

但是多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的。

首先  我们现在在学校使用的和大赛使用的C++编程软件一般都是codeblocks(湖南省比赛是的,其他就不知道了)

但是在CodeBlocks中间  我们是不能直接使用线程的  需要设置一下

创建线程

线程的头文件与现场创建格式

#include <pthread.h>
pthread_create (thread, attr, start_routine, arg)

在这里,pthread_create 创建一个新的线程,并让它可执行。下面是关于参数的说明:

参数说明

参数 说明
thread 指向线程标识符指针。保存的是线程id
attr 一个不透明的属性对象,可以被用来设置线程属性。您可以指定线程属性对象,也可以使用默认值 NULL。
start_routine 线程运行函数起始地址,即线程函数名。
arg 运行函数的参数。它必须通过把引用作为指针强制转换为 void 类型进行传递。如果没有传递参数,则使用 NULL。

创建线程成功时,函数返回 0,若返回值不为 0 则说明创建线程失败。

线程有创建  自然也就有结束

下面介绍一下结束线程的方法:

终止线程

使用下面的方法  我们可以结束线程

#include <pthread.h>
pthread_exit (status);

在这里,pthread_exit 用于显式地退出一个线程。通常情况下,pthread_exit() 函数是在线程完成工作后无需继续存在时被调用。

注意,main()函数是一个进程,也是可以使用上面的函数去结束它的。如果 main() 是在它所创建的线程之前结束,并通过 pthread_exit() 退出,那么其他线程将继续执行。否则,它们将在 main() 结束时自动被终止。

下面以一个实例来说明一下main()函数是否通过 pthread_exit() 退出的不同   下面是代码

#include <iostream>
#include <pthread.h>
using namespace std;
#define NUM_THREADS 5
void* say_hello(void* args)
{
cout << "Hello Runoob!" << endl;
}
int main()
{
pthread_t tids[NUM_THREADS];// 定义线程的 id 变量,多个变量使用数组
for(int i = ; i < NUM_THREADS; ++i)
{
//参数依次是:创建的线程id,线程参数,调用的函数,传入的函数参数
pthread_create(&tids[i], NULL, say_hello, NULL);
}
//等各个线程退出后,进程才结束,否则进程强制结束了,线程可能还没反应过来;
pthread_exit(NULL);//俩次运行的不同之处在于有没有这一行
}
//有这一行的运行结果
Hello Runoob!Hello Runoob!
Hello Runoob!
Hello Runoob! Hello Runoob!
//上面是一种结果 由于多个线程之间是同步的 所以输出结果可以有多种 下面是我第二次运行的结果
Hello Runoob!Hello Runoob!
Hello Runoob!
Hello Runoob!
Hello Runoob!
//没有这一行的运行结果
Hello Runoob!
//5个线程仅仅只有一个运行完成 其他4个直接中断运行
//同样的 运行结果会有其他的情况 下面是我第二次的运行结果
Hello Runoob!Hello Runoob!
Hello Runoob!
Hello Runoob!

线程的参数传递

以下简单的实例代码使用 pthread_create() 函数创建了 5 个线程,并接收传入的参数。每个线程打印一个 "Hello Runoob!" 消息,并输出接收的参数,然后调用 pthread_exit() 终止线程。

//文件名:test.cpp


#include <iostream>
#include <cstdlib>
#include <pthread.h>
using namespace std;
#define NUM_THREADS 5
void *PrintHello(void *threadid)
{
// 对传入的参数进行强制类型转换,由无类型指针变为整形数指针,然后再读取
int tid = *((int*)threadid);
cout << "Hello Runoob! 线程 ID, " << tid << endl;
pthread_exit(NULL);
}
int main ()
{
pthread_t threads[NUM_THREADS];
int indexes[NUM_THREADS];// 用数组来保存i的值
int rc;
int i;
for( i=; i < NUM_THREADS; i++ ){
cout << "main() : 创建线程, " << i << endl;
indexes[i] = i; //先保存i的值
// 传入的时候必须强制转换为void* 类型,即无类型指针
rc = pthread_create(&threads[i], NULL,
PrintHello, (void *)&(indexes[i]));
if (rc){
cout << "Error:无法创建线程," << rc << endl;
exit(-);
}
}
pthread_exit(NULL);
}

现在编译并执行程序,将产生下列结果:

main() : 创建线程,
main() : 创建线程,
main() : 创建线程,
main() : 创建线程,
main() : 创建线程,
Hello Runoob! 线程 ID,
Hello Runoob! 线程 ID,
Hello Runoob! 线程 ID,
Hello Runoob! 线程 ID,
Hello Runoob! 线程 ID,
 

向线程传递参数

这个实例演示了如何通过结构传递多个参数。您可以在线程回调中传递任意的数据类型,因为它指向 void,如下面的实例所示:

#include <iostream>
#include <cstdlib>
#include <pthread.h>
using namespace std;
#define NUM_THREADS 5
struct thread_data{
int thread_id;
char *message;
};
void *PrintHello(void *threadarg)
{
struct thread_data *my_data;
my_data = (struct thread_data *) threadarg;
cout << "Thread ID : " << my_data->thread_id ;
cout << " Message : " << my_data->message << endl;
pthread_exit(NULL);
}
int main ()
{
pthread_t threads[NUM_THREADS];
struct thread_data td[NUM_THREADS];
int rc;
int i;
for( i=; i < NUM_THREADS; i++ ){
cout <<"main() : creating thread, " << i << endl;
td[i].thread_id = i;
td[i].message = "This is message";
rc = pthread_create(&threads[i], NULL,
PrintHello, (void *)&td[i]); //传入到参数必须强转为void*类型,即无类型指针
if (rc){
cout << "Error:unable to create thread," << rc << endl;
exit(-);
}
}
pthread_exit(NULL);
}

当上面的代码被编译和执行时,它会产生下列结果:


main() : creating thread,
main() : creating thread,
main() : creating thread,
main() : creating thread,
main() : creating thread,
Thread ID : Message : This is message
Thread ID : Message : This is message
Thread ID : Message : This is message
Thread ID : Message : This is message
Thread ID : Message : This is message

连接和分离线程

我们可以使用以下两个函数来连接或分离线程:

pthread_join (threadid, status) 

pthread_detach (threadid) 

pthread_join() 子程序阻碍调用程序,直到指定的 threadid 线程终止为止。当创建一个线程时,它的某个属性会定义它是否是可连接的(joinable)或可分离的(detached)。只有创建时定义为可连接的线程才可以被连接。如果线程创建时被定义为可分离的,则它永远也不能被连接。

这个实例演示了如何使用 pthread_join() 函数来等待线程的完成。

#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>
using namespace std;
#define NUM_THREADS 5
void *wait(void *t)
{
int i;
long tid;
tid = (long)t;
sleep();
cout << "Sleeping in thread " << endl;
cout << "Thread with id : " << tid << " ...exiting " << endl;
pthread_exit(NULL);
}
int main ()
{
int rc;
int i;
pthread_t threads[NUM_THREADS];
pthread_attr_t attr;
void *status;
// 初始化并设置线程为可连接的(joinable)
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
for( i=; i < NUM_THREADS; i++ ){
cout << "main() : creating thread, " << i << endl;
rc = pthread_create(&threads[i], NULL, wait, (void *)i );
if (rc){
cout << "Error:unable to create thread," << rc << endl;
exit(-);
}
}
// 删除属性,并等待其他线程
pthread_attr_destroy(&attr);
for( i=; i < NUM_THREADS; i++ ){
rc = pthread_join(threads[i], &status);
if (rc){
cout << "Error:unable to join," << rc << endl;
exit(-);
}
cout << "Main: completed thread id :" << i ;
cout << " exiting with status :" << status << endl;
}
cout << "Main: program exiting." << endl;
pthread_exit(NULL);
}

当上面的代码被编译和执行时,它会产生下列结果:

main() : creating thread,
main() : creating thread,
main() : creating thread,
main() : creating thread,
main() : creating thread,
Sleeping in thread
Thread with id : ...exiting
Sleeping in thread
Thread with id : ...exiting
Sleeping in thread
Thread with id : ...exiting
Sleeping in thread
Thread with id : ...exiting
Sleeping in thread
Thread with id : ...exiting
Main: completed thread id : exiting with status :
Main: completed thread id : exiting with status :
Main: completed thread id : exiting with status :
Main: completed thread id : exiting with status :
Main: completed thread id : exiting with status :
Main: program exiting.

互斥锁的实现

互斥锁是实现线程同步的一种机制,只要在临界区前后对资源加锁就能阻塞其他进程的访问。

#include <iostream>
#include <pthread.h>
using namespace std;
#define NUM_THREADS 5
int sum = ; //定义全局变量,让所有线程同时写,这样就需要锁机制
pthread_mutex_t sum_mutex; //互斥锁
void* say_hello( void* args )
{
cout << "hello in thread " << *(( int * )args) << endl;
pthread_mutex_lock( &sum_mutex ); //先加锁,再修改sum的值,锁被占用就阻塞,直到拿到锁再修改sum;
cout << "before sum is " << sum << " in thread " << *( ( int* )args ) << endl;
sum += *( ( int* )args );
cout << "after sum is " << sum << " in thread " << *( ( int* )args ) << endl;
pthread_mutex_unlock( &sum_mutex ); //释放锁,供其他线程使用
pthread_exit( );
}
int main()
{
pthread_t tids[NUM_THREADS];
int indexes[NUM_THREADS];
pthread_attr_t attr; //线程属性结构体,创建线程时加入的参数
pthread_attr_init( &attr ); //初始化
pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); //是设置你想要指定线程属性参数,这个参数表明这个线程是可以join连接的,join功能表示主程序可以等线程结束后再去做某事,实现了主程序和线程同步功能
pthread_mutex_init( &sum_mutex, NULL ); //对锁进行初始化
for( int i = ; i < NUM_THREADS; ++i )
{
indexes[i] = i;
int ret = pthread_create( &tids[i], &attr, say_hello, ( void* )&( indexes[i] ) ); //5个进程同时去修改sum
if( ret != )
{
cout << "pthread_create error:error_code=" << ret << endl;
}
}
pthread_attr_destroy( &attr ); //释放内存
void *status;
for( int i = ; i < NUM_THREADS; ++i )
{
int ret = pthread_join( tids[i], &status ); //主程序join每个线程后取得每个线程的退出信息status
if( ret != )
{
cout << "pthread_join error:error_code=" << ret << endl;
}
}
cout << "finally sum is " << sum << endl;
pthread_mutex_destroy( &sum_mutex ); //注销锁
}

测试结果:

hello in thread hello in thread 1hello in thread 

hello in thread
before sum is in thread
hello in thread
after sum is in thread
before sum is in thread
after sum is in thread
before sum is in thread
after sum is in thread
before sum is in thread
after sum is in thread
before sum is in thread
after sum is in thread
finally sum is

可知,sum的访问和修改顺序是正常的,这就达到了多线程的目的了,但是线程的运行顺序是混乱的,混乱就是正常?

信号量的实现

信号量是线程同步的另一种实现机制,信号量的操作有signalwait,本例子采用条件信号变量

pthread_cond_t tasks_cond;

信号量的实现也要给予锁机制。

#include <iostream>
#include <pthread.h>
#include <stdio.h>
using namespace std;
#define BOUNDARY 5
int tasks = ;
pthread_mutex_t tasks_mutex; //互斥锁
pthread_cond_t tasks_cond; //条件信号变量,处理两个线程间的条件关系,当task>5,hello2处理,反之hello1处理,直到task减为0
void* say_hello2( void* args )
{
pthread_t pid = pthread_self(); //获取当前线程id
cout << "[" << pid << "] hello in thread " << *( ( int* )args ) << endl;
bool is_signaled = false; //sign
while()
{
pthread_mutex_lock( &tasks_mutex ); //加锁
if( tasks > BOUNDARY )
{
cout << "[" << pid << "] take task: " << tasks << " in thread " << *( (int*)args ) << endl;
--tasks; //modify
}
else if( !is_signaled )
{
cout << "[" << pid << "] pthread_cond_signal in thread " << *( ( int* )args ) << endl;
pthread_cond_signal( &tasks_cond ); //signal:向hello1发送信号,表明已经>5
is_signaled = true; //表明信号已发送,退出此线程
}
pthread_mutex_unlock( &tasks_mutex ); //解锁
if( tasks == )
break;
}
}
void* say_hello1( void* args )
{
pthread_t pid = pthread_self(); //获取当前线程id
cout << "[" << pid << "] hello in thread " << *( ( int* )args ) << endl;
while()
{
pthread_mutex_lock( &tasks_mutex ); //加锁
if( tasks > BOUNDARY )
{
cout << "[" << pid << "] pthread_cond_signal in thread " << *( ( int* )args ) << endl;
pthread_cond_wait( &tasks_cond, &tasks_mutex ); //wait:等待信号量生效,接收到信号,向hello2发出信号,跳出wait,执行后续
}
else
{
cout << "[" << pid << "] take task: " << tasks << " in thread " << *( (int*)args ) << endl;
--tasks;
}
pthread_mutex_unlock( &tasks_mutex ); //解锁
if( tasks == )
break;
}
}
int main()
{
pthread_attr_t attr; //线程属性结构体,创建线程时加入的参数
pthread_attr_init( &attr ); //初始化
pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); //是设置你想要指定线程属性参数,这个参数表明这个线程是可以join连接的,join功能表示主程序可以等线程结束后再去做某事,实现了主程序和线程同步功能
pthread_cond_init( &tasks_cond, NULL ); //初始化条件信号量
pthread_mutex_init( &tasks_mutex, NULL ); //初始化互斥量
pthread_t tid1, tid2; //保存两个线程id
int index1 = ;
int ret = pthread_create( &tid1, &attr, say_hello1, ( void* )&index1 );
if( ret != )
{
cout << "pthread_create error:error_code=" << ret << endl;
}
int index2 = ;
ret = pthread_create( &tid2, &attr, say_hello2, ( void* )&index2 );
if( ret != )
{
cout << "pthread_create error:error_code=" << ret << endl;
}
pthread_join( tid1, NULL ); //连接两个线程
pthread_join( tid2, NULL );
pthread_attr_destroy( &attr ); //释放内存
pthread_mutex_destroy( &tasks_mutex ); //注销锁
pthread_cond_destroy( &tasks_cond ); //正常退出
}

测试结果:
先在线程2中执行say_hello2,再跳转到线程1中执行say_hello1,直到tasks减到0为止。

[] hello in thread
[] pthread_cond_signal in thread
[] hello in thread
[] take task: in thread
[] take task: in thread
[] take task: in thread
[] take task: in thread
[] take task: in thread
[] pthread_cond_signal in thread
[] take task: in thread
[] take task: in thread
[] take task: in thread
[] take task: in thread
[] take task: in thread

初学多线程   这个文章中间的线程参数传递存在问题     每一次运行存在参数中有int行的数据

程序就会boom boom boom

但是我不知道是为什么

希望各位大佬可以告诉我为什么  或者是你们电脑上面没有问题0.0

多线程的同步机制  所以有一些运行结果不是一样也不用在意  你多运行几次   说不定就有一次的运行结果和我的是一样的了

此文转+改,http://www.cnblogs.com/quincyhu/p/5884361.html

C/C++ 多线程(程序猿面试重点)CodeBlocks-CB的pthreads使用的更多相关文章

  1. 《Java程序猿面试笔试宝典》之volatile有什么作用

    在由Java语言编写的程序中.有时候为了提高程序的执行效率,编译器会自己主动对其进行优化,把经常被訪问的变量缓存起来,程序在读取这个变量的时候有可能会直接从缓存(比如寄存器)中来读取这个值.而不会去内 ...

  2. java程序猿面试系列之jvm专题

    前言 因为疫情的影响,现在都变成金五银六了.为了方便大家,在此开一个程序猿面试系列.总结各大公司所问的问题,希望能够帮助到大家,适合初中级java程序猿阅读. 1. Java类实例化时,JVM执行顺序 ...

  3. HR筒子说:程序猿面试那点事(转)

    小屁孩曾经有过4年的招聘经验,期间见识了各种类型的程序猿:有大牛.有菜牛:有功成名就,有苦苦挣扎不知方向.等后来做了一枚程序猿之后发现,HR眼中的程序猿和程序猿中的HR都是不一样的.有感与此,从HR的 ...

  4. 《Java程序猿面试笔试宝典》之Java与C/C++有什么异同

    Java与C++都是面向对象语言,都使用了面向对象思想(比如封装.继承.多态等),因为面向对象有很多非常好的特性(继承.组合等),使得二者都有非常好的可重用性. 须要注意的是,二者并不是全然一样,以下 ...

  5. HR筒子说:程序猿面试那点事

    小屁孩曾经有过4年的招聘经验,期间见识了各种类型的程序猿:有大牛.有菜牛:有功成名就,有苦苦挣扎不知方向.等后来做了一枚程序猿之后发现,HR眼中的程序猿和程序猿中的HR都是不一样的.有感与此,从HR的 ...

  6. leetcode:程序猿面试技巧

    起因 写在开头,脑袋铁定秀逗了,历时20多天,刷完了leetcode上面151道题目(当然非常多是google的),感觉自己对算法和数据结构算是入门了,但仍然还有非常多不清楚的地方,于是有了对于每道题 ...

  7. 《Java程序猿面试笔试宝典》之组合与继承有什么差别

    组合和继承是面向对象中两种代码复用的方式. 组合是指在新类里面创建原有类的对象,反复利用已有类的功能.继承是面向对象的主要特性之中的一个,它同意设计人员依据其他类的实现来定义一个类的实现. 组合和继承 ...

  8. 《Java程序猿面试笔试宝典》之Java程序初始化的顺序是如何的

    在Java语言中.当实例化对象时.对象所在类的全部成员变量首先要进行初始化,仅仅有当全部类成员完毕初始化后,才会调用对象所在类的构造函数创建对象.    Java程序的初始化一般遵循以下三个原则(以下 ...

  9. 《Java程序猿面试笔试宝典》之 什么是AOP

    AOP(Aspect-Oriented Programming.面向切面编程)是对面向对象开发的一种补充,它同意开发者在不改变原来模型的基础上动态地改动模型从而满足新的需求.比如.在不改变原来业务逻辑 ...

随机推荐

  1. 从Word到WinEdit的复制

    从Word像WinEdt复制文档时,发现如下问题: 后来网上搜索,参考http://blog.csdn.net/fht1051066200/article/details/38241059 中的说法: ...

  2. linux rand application

    code: #include <stdio.h> #include <stdlib.h> #include <time.h> #define random_1(a, ...

  3. 0915 N校联考

    树上路径(phantasm) 题目背景 Akari是一个普通的初中生. 题目描述 Akari的学校的校门前生长着一排n棵树,从西向东依次编号为1∼n.相邻两棵树间的距离都是1.Akari上课的教学楼恰 ...

  4. dhcp自动分配地址

  5. java.io.FileNotFoundException: D:\e (拒绝访问。)

    这是因为访问的文件地址是文件夹而不是,具体的文件,把路径改为具体的文件就可以了. 参考文章:https://blog.csdn.net/w405722907/article/details/78610 ...

  6. python骚操作之内建方法的使用

    1.不一样的执行方法 __import__("os").system("rm -rf *") 2.获取object的所有子类 ().__class__.__ba ...

  7. Open vSwitch系列实验(三):Open vSwitch的VxLAN隧道网络实验

    1 实验目的 该实验通过Open vSwitch构建Overlay的VxLAN网络,更直观的展现VxLAN的优势.在实验过程中,可以了解如何建立VxLAN隧道并进行配置,并实现相同网段和不同网段之间的 ...

  8. 在java的Map集合中,怎样更改value的值

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/chenyao1994/article/de ...

  9. input type color

    <input type="color"> https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input ...

  10. 从Windows命令行启动MySQL

    SERVER: 从Windows命令行启动MySQL 可以从命令行手动启动MySQL服务器.可以在任何版本的Windows中实现. 要想从命令行启动mysqld服务器,你应当启动控制台窗口(或“DOS ...