Posix线程和C++11多线程学习_posix的pthread转换成c++11的mutex(2)

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!



$ g++ posix_thread_01.cpp -o posix_thread_01 -lpthread
$ ./posix_thread_01
Launched by thread:140300023379712
Launched by thread:140300014987008
Launched by thread:140300006594304
Launched by thread:140299998201600
Launched from the main
Launched by thread:140300031772416


当创建多个子线程时,外加主线程,共计6个线程,我们可以看到线程之间是平等竞争临界资源关系,顺序是无序的,因此如果要明确确定顺序,则需要同步等barrier术语来改进。


###### 线程传参的实现


线程的传参可以是简单的数据类型,也可以是自定义的数据结构,因此我们在编写线程传参的时候要留意这些。



//Create a group of Posix threads from the main program
#include
#include <pthread.h>
static const int num_threads = 5;

typedef struct {
int thread_id;
} thread_data;

//This function will be called from a thread

void *call_from_thread(void *args) {
thread_data *my_data = (thread_data *) args;
std::cout << "Launched by thread " << my_data->thread_id << std::endl;
return NULL;
}

int main() {
pthread_t t[num_threads];
thread_data td[num_threads];

//Launch a group of threads
for (int i = 0; i < num_threads; ++i) {
    td[i].thread_id = i;
    pthread\_create(&t[i], NULL, call_from_thread, (void \*) &td[i]);
}

std::cout << "Launched from the main\n";

//Join the threads with the main thread
for (int i = 0; i < num_threads; ++i) {
    pthread\_join(t[i], NULL);
}

return 0;

}



$ g++ posix_thread_02.cpp -o posix_thread_02 -lpthread
$ ./posix_thread_02
Launched by thread 0
Launched from the main
Launched by thread 1
Launched by thread 2
Launched by thread 4
Launched by thread 3


再一次验证了线程之间是竞争无序的。


##### 二、C++ threads多线程实践


C++11增加了多线程支持,能够更好地在多核计算机上发挥性能。


###### 一个简单线程的实现


编译时记得添加`-std=c++11`选项



//Create a C++11 thread from the main program

#include
#include

//This function will be called from a thread
void call_from_thread() {
std::cout << “Hello, World!” << std::endl;
}

int main() {
//Launch a thread
std::thread t1(call_from_thread);

//Join the thread with the main thread
t1.join();

return 0;

}

//lambda格式
auto func = [] (string name) {
cout << “hello” << name << endl;
};
thread t(func,“tom”);
t.join();


我们可以和上文使用POSIX线程API创建的线程比较,本质是一样的。  
 main函数创建了线程`call_from_thread`,并在`t1.join()`处完成。如果你忘记等待,main线程有可能先完成,此时程序退出时可能会杀死之前所创建的线程,而不管子线程是否已完成。


###### 多个线程的实现



//Create a group of C++11 threads from the main program

#include
#include

static const int num_threads = 10;

//This function will be called from a thread

void call_from_thread() {
std::cout << “Launched by thread\n”;
}

int main() {
std::thread t[num_threads];

//Launch a group of threads
for (int i = 0; i < num_threads; ++i) {
    t[i] = std::thread(call_from_thread);
}

std::cout << "Launched from the main\n";

//Join the threads with the main thread
for (int i = 0; i < num_threads; ++i) {
    t[i].join();
}

return 0;

}


main函数也是一个线程,通常命名为主线程,因此上述代码实际运行了11个线程,我们也可以在主线程启动子线程后做一些工作。  
 多个线程同时运行后没有特定的顺序,我们在编程时要确保线程不会修改临界的资源。如果多个线程之间确实要竞争一些资源,我们需要编写更复杂的并行代码,使用类似mutex、atomic等方法避免上述问题。


###### 线程传参的实现



#include
#include
#include

void thread_fun(std::string &str) {
std::cout << “thread fun”;
std::cout << "msg is = " << str << std::endl;
str = “hello”;
}

int main()
{
std::string str = “katty”;
std::thread t(&thread_fun,std::ref(str));
std::cout<< "main thread = " << str << std::endl;
t.join();
return 0;
}


这里使用的是Linux g++编译器,使用了一些C++11新特性,大家根据自己的需要更改代码。  
 当然thread\_fun也可以更改为lambdas 匿名函数实现。


###### 四、类成员函数作为线程函数的实现


其实这次项目中真正遇到的是这个问题,如何将一个类成员函数作为一个线程函数实现呢?


很显然,因为成员方法可以作为线程体来执行,所以获得了如下好处:


* 线程的变量传递非常方便,直接读写成员变量即可;
* 线程体作为对象的一部分,可以访问对象的私有变量和方法。


在参考了文章:[成员函数作为pthread线程](https://bbs.csdn.net/topics/618668825)  
 之后,我选择使用pthread和C++11 threads来实现之。


###### 成员函数作为线程(POSIX threads)


我们再次复习下pthread的API接口:



int pthread_create(pthread_t *thread,pthread_attr_t *attr,void *(*routine)(void *),void *arg);


主要第三个参数routine是一个普通函数,而不能是一个类成员函数,这是为什么呢?  
 这里来测试下:



class A
{
public:
A(const char* name);
void sayHello();
void stop();
private:
const char* m_name ;
pthread_t m_thread;
};

A::A(const char*name)
{
m_name = name;

pthread\_create(&m_thread,0,sayHello,this);

}


编译报错:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20181111210620261.png)  
 看到了吧:成员函数默认是非静态函数,我们这样做编译失败。  
 那么我们能否尝试将pthread\_create的第三个参数改为静态函数呢?



static void* createThread(void arg)
{
A pa = (A)arg;
pa -> sayHello();
return (void
)0;
}
A::A(const char*name)
{
m_name = name;

pthread_create(&m_thread,0,createThread,this);

}


可以看到,我们通过static函数`createThread`作为第三个参数,同时使用第四个参数`this`线程传参,进而在`createThread`间接调用class中的成员方法,测试是可行的。


总结:常规方法是两种:



> 
> 1. 将routine声明为该类的友元函数
> 2. 将routine声明为静态函数
> 
> 
> 


那我们能否有其他的办法呢?  
 答案就是利用lambdas函数。



#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

class A
{
public:
A(const char* name);
void sayHello();
void stop();
private:
const char* m_name ;
pthread_t m_thread;
};

A::A(const char*name)
{
m_name = name;
//call class method

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

ss method

[外链图片转存中…(img-isyT35xa-1715840411659)]
[外链图片转存中…(img-GxbYwEVo-1715840411659)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值