c++多线程初步学习

c++多线程学习

学习资料来源于C++多线程菜鸟教程:c++多线程

本人使用环境 manjaro  ide为vscode  c++11,

这里需要注意,对于线程相关代码编译时需加入 -lpthread 即 g++ a.cpp -lpthread

否则会显示undefined reference to `pthread_create'

我这里使用为vscode按照刚开始配置文件无法通过vsc直接编译,但是可以修改tasks.json文件

在args里面加入 "-lpthread"即可  以下给我我的配置文件 修改后可直接编译运行

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Compile", // 任务名称,与launch.json的preLaunchTask相对应
            "command": "clang++", // 要使用的编译器
            "args": [
                "${file}",
                "-o", // 指定输出文件名,不加该参数则默认输出a.exe,Linux下默认a.out
                "${fileDirname}/${fileBasenameNoExtension}.exe",
                "-g", // 生成和调试有关的信息
                "-Wall", // 开启额外警告
                "-static-libgcc", // 静态链接
                "-lpthread",
                "-fcolor-diagnostics", // 彩色的错误信息?但貌似clang默认开启而gcc不接受此参数
                "-std=c++11" // C语言最新标准为c11,或根据自己的需要进行修改
            ], // 编译命令参数
            "type": "shell", // 可以为shell或process,前者相当于先打开shell再输入命令,后者是直接运行命令
            "group": {
                "kind": "build",
                "isDefault": true // 设为false可做到一个tasks.json配置多个编译指令,需要自己修改本文件,我这里不多提
            },
            "presentation": {
                "echo": true,
                "reveal": "always", // 在“终端”中显示编译信息的策略,可以为always,silent,never。具体参见VSC的文档
                "focus": false, // 设为true后可以使执行task时焦点聚集在终端,但对编译c和c++来说,设为true没有意义
                "panel": "shared" // 不同的文件的编译信息共享一个终端面板
            }
            // "problemMatcher":"$gcc" // 如果你不使用clang,去掉前面的注释符,并在上一条之后加个逗号。照着我的教程做的不需要改(也可以把这行删去)
        }
    ]
}

接下开始学习多线程:

首先是线程的创建

pthread_create (thread, attr, start_routine, arg) 

该函数有4个参数,分别代表

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

看起来可能不是很清楚,直接上代码。

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

这里有个问题,如果pringhello函数里没有pthread_exit()函数的话,程序会运行到一半正常终止,目前我也不知道是什么原因,猜测可能是线程没有运行完结果进程直接结束了,导致后续线程无法运行。

可以看到4个参数中,第3个参数代表该线程需要完成的任务,而最后一个参数是将在线程开始函数的传入的参数,那么有个问题,如何给函数传入多个参数呢,

#include <bits/stdc++.h>
using namespace std;
const int maxn=5;

struct thread_data//创建一个结构体 格式为将传入的参数
{
    int thread_id;
    char *message;
};
void *print(void *threadarg)//函数这里传入的仍未空指针
{ 
    thread_data *mydata;
    mydata=(thread_data*) threadarg;//通过强制类型转换将参数列表提取出来
    cout<<"Thread ID: "<<mydata->thread_id<<endl;
    cout<<"Thread message: "<<mydata->message<<endl;
    pthread_exit(NULL);
}
int main()
{
    pthread_t tids[maxn];
    thread_data td[maxn];//输入参数
    for(int i=0;i<5;i++)
    {
        cout<<"main() : create thread, "<<i<<endl;
        td[i].thread_id=i;
        td[i].message="makise kurisu";
        int rc=pthread_create(&tids[i],NULL,print,(void*)&td[i]);//参数强转为void*
        if(rc)
        {
            cout<<"error"<<endl;
            exit(-1);
        }
    }
    pthread_exit(NULL);
}

可以看到 通过类型强转实现了函数传入多个参数的方法,目前我只学习了这一个写法,不清楚有没有其他的,日后在更,现在的目的只是单纯的了解多线程的基础操作和原理。

线程的属性由结构体pthread_attr_t管理,

typedef struct
{
    int                           detachstate;     线程的分离状态
    int                          schedpolicy;   线程调度策略
    struct sched_param      schedparam;   线程的调度参数
    int inheritsched; 线程的继承性 
    int scope; 线程的作用域 
    size_t guardsize; 线程栈末尾的警戒缓冲区大小 
    int stackaddr_set; void * stackaddr; 线程栈的位置 
    size_t stacksize; 线程栈的大小
}pthread_attr_t;

使用join来连接线程

#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>
 
using namespace std;
 
#define NUM_THREADS     5
 
void *wait(void *t)
{
   int tid= *((int*)t);
   sleep(1);
   cout << "Sleeping in thread " << endl;
   int status = 10 + *(( int * )t); //线程退出时添加退出的信息,status供主程序提取该线程的结束信息
   pthread_exit( ( void* )status ); 
}
int main ()
{
   int arr[NUM_THREADS];
   pthread_t threads[NUM_THREADS];
   pthread_attr_t attr;
   void *status;
   // 初始化并设置线程为可连接的(joinable)
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
   //该行表示将该参数设置为可连接的,即主程序进程会等线程运行完再去执行 实现同步
   for(int i=0; i < NUM_THREADS; i++ )
   {
      cout << "main() : creating thread, " << i << endl;
      arr[i]=i;
      int rc = pthread_create(&threads[i], NULL, wait, (void *)&arr[i] );
      if (rc)
      {
         cout << "Error:unable to create thread," << rc << endl;
         exit(-1);
      }
   }
   // 删除属性,并等待其他线程
   pthread_attr_destroy(&attr);
   for( int i = 0; i < NUM_THREADS; ++i )
   {
	   int rc = pthread_join( threads[i], &status ); //主程序join每个线程后取得每个线程的退出信息status
	   if(rc) cout << "pthread_join error:error_code=" << rc << endl;
      else cout << "pthread_join get status:" << (long)status << endl;
   }
   cout << "Main: program exiting." << endl;
   pthread_exit(NULL);
}

输出结果

main() : creating thread, 0
main() : creating thread, 1
main() : creating thread, 2
main() : creating thread, 3
main() : creating thread, 4
Sleeping in thread 
Sleeping in thread 
Sleeping in thread 
Sleeping in thread 
Sleeping in thread 
pthread_join get status:10
pthread_join get status:11
pthread_join get status:12
pthread_join get status:13
pthread_join get status:14
Main: program exiting.

互斥锁机制

#include<bits/stdc++.h>
using namespace std;
const int maxn=5;
int sum=0; //创建全局变量 所有线程同时写 需要锁机制
pthread_mutex_t sum_mutex;//创建互斥锁
void* say(void *args)
{
   cout<<"hello in thread "<< *((int*)args)<<endl;
   pthread_mutex_lock(&sum_mutex);//加锁 使得其他进程无法同时修改sum值
   cout<<"before sum is "<<sum<<" in thread "<< *((int*)args)<<endl;
   sum+= 5;
   cout<<"after sum is "<<sum<<" in thread "<<*((int*)args)<<endl;
   pthread_mutex_unlock(&sum_mutex);//释放锁 供其他线程使用
   pthread_exit(0);
}
int main()
{
   pthread_t tids[maxn];
   int idx[maxn];
   pthread_attr_t attr;//创建线程属性
   pthread_attr_init(&attr);//初始化
   pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);
   //设置为连接属性
   pthread_mutex_init(&sum_mutex,NULL);//初始化锁
   for(int i=0;i<maxn;i++)
   {
      idx[i]=i;
      int rc=pthread_create(&tids[i],&attr,say,(void*)(&idx[i]));
      if(rc) cout<<"thread error:"<<rc<<endl;
   }
   pthread_attr_destroy(&attr);//释放内存
   void *status;
   for(int i=0;i<maxn;i++)
   {
      int rc=pthread_join(tids[i],&status);
      if(rc) cout<<"pthread_join error"<<endl;
   }
   cout<<"finally sum is= "<<sum<<endl;
   pthread_mutex_destroy(&sum_mutex);//注销锁
}

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

hello in thread 0
before sum is 0 in thread 0
after sum is 5 in thread 0
hello in thread 1
before sum is 5 in thread 1
after sum is 10 in thread 1
hello in thread 2
before sum is 10 in thread 2
after sum is 15 in thread 2
hello in thread 3
before sum is 15 in thread 3
after sum is 20 in thread 3
hello in thread 4
before sum is 20 in thread 4
after sum is 25 in thread 4
finally sum is= 25

运气比较好,每次的线程都是按顺序运行,理论上可以出现线程运行顺序不同的情况,但是sum的修改过程是一定按照顺序来的

 

信号量

介绍信号量前先介绍几种基础的操作

pthread_cond_t sign//新建信号量

pthread_cond_wait(&tasks_cond,&tasks_mutex);

主要介绍一下该函数,该函数有两个参数,第一个参数为信号量,它首先将当前线程加入到唤醒队列,然后旋即解锁mutex锁,最后等待被唤醒。被唤醒后,又对mutex加锁(可能是看起来没有对用户的行为作任何的改变)。

这样的话我们使用这个函数前就需要对mutex加锁,防止出现多线程等待的情况。

#include<bits/stdc++.h>
using namespace std;
const int maxn=5;
int tasks=10;
pthread_mutex_t tasks_mutex;//互斥锁
pthread_cond_t tasks_cond; //条件信号变量 tasks大于5 say_2处理 否则 1处理
void* say_2(void *args)
{
   pthread_t pid=pthread_self();//获取当前线程id
   cout << "[" << pid << "] hello in thread " <<  *( ( int* )args ) << endl;
   bool sign=false;//sign
   while(1)
   {
      pthread_mutex_lock(&tasks_mutex);//加锁
      if(tasks>maxn)
      {
         cout << "[" << pid << "] take task: " << tasks << " in thread " << *( (int*)args ) << endl;
         --tasks;
      }
      else if(!sign)
      {
         cout << "[" << pid << "] pthread_cond_signal in thread " << *( ( int* )args ) << endl;
         pthread_cond_signal(&tasks_cond);//发送信号 表示已经大于5
         sign=true;//信号已发送 退出该线程
      }
      pthread_mutex_unlock(&tasks_mutex);//解锁
      if(tasks==0) break;
   }
   pthread_exit(0);
}
void* say_1(void *args)
{
   pthread_t pid=pthread_self();
   cout << "[" << pid << "] hello in thread " <<  *( ( int* )args ) << endl;
   while(1)
   {
      pthread_mutex_lock(&tasks_mutex);//加锁
      if(tasks>maxn)
      {
         cout << "[" << pid << "] pthread_cond_signal in thread " << *( ( int* )args ) << endl;
         pthread_cond_wait(&tasks_cond,&tasks_mutex);
         cout<<"---"<<endl;
         //等待信号量生效 接收到信号 向say_2发送信号 跳出wait等待后续
      }
      else
      {
         cout << "[" << pid << "] take task: " << tasks << " in thread " << *( (int*)args ) << endl;
         --tasks;
      }
      pthread_mutex_unlock(&tasks_mutex);//解锁
      if(tasks==0) break;
   }
   pthread_exit(0);
}
int main()
{
   pthread_attr_t attr;
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);
   pthread_cond_init(&tasks_cond,NULL);
   pthread_mutex_init(&tasks_mutex,NULL);
   pthread_t pid1,pid2;
   int idx1=1;
   int rc=pthread_create(&pid1,&attr,say_1,(void*)&idx1);
   if(rc) cout<<"error!"<<endl;
   int idx2=2;
   rc=pthread_create(&pid2,&attr,say_2,(void*)&idx2);
   if(rc) cout<<"error!"<<endl;
   pthread_join(pid1,NULL);//连接两个线程
   pthread_join(pid2,NULL);
   pthread_attr_destroy(&attr);
   pthread_mutex_destroy(&tasks_mutex);
   pthread_cond_destroy(&tasks_cond);
}
[140737348364032] hello in thread 1
[140737348364032] pthread_cond_signal in thread 1
[140737339971328] hello in thread 2
[140737339971328] take task: 10 in thread 2
[140737339971328] take task: 9 in thread 2
[140737339971328] take task: 8 in thread 2
[140737339971328] take task: 7 in thread 2
[140737339971328] take task: 6 in thread 2
[140737339971328] pthread_cond_signal in thread 2
---
[140737348364032] take task: 5 in thread 1
[140737348364032] take task: 4 in thread 1
[140737348364032] take task: 3 in thread 1
[140737348364032] take task: 2 in thread 1
[140737348364032] take task: 1 in thread 1

通过上述代码可以发现,开始运行线程1,然后运行到pthread_cond_wait(&tasks_cond,&tasks_mutex);,该线程进入唤醒状态,等待信号量生效,同时从该位置终止线程,进入线程2,完成任务后(即tasks小于5),发送信号量,同时该线程2结束,进入线程1等待的位置重新运行。

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值