网易云课堂上有门c++多线程的入门教程,学习过程在此记录下。
并发编程:
1.多进程(通信:文件,管道,消息队列);2.多线程(通信:共享内存)。
code1:
#include
#include
void test_funtion(){
std::cout << "test test test!" < std::thread t1(test_funtion);
//t1.join();//main线程等待t1线程完成后继续向下执行
t1.detach();//t1还没来得及输出,主程序就结束了,所以没有输出。线程detach之后,不能再join。
if (t1.joinable()){
t1.join();
}
return 0;
}
code2:
#include
#include
void test_funtion(){
std::cout << "test_funtion!" < std::thread t1(test_funtion);
for (int i = 0; i < 100;++i)
{
std::cout << "main count i:" < }
t1.join();
return 0;
}
如果在for循环中,程序抛出异常,那程序是否会直接结束了,不会执行join?
#include
#include
void test_funtion(){
std::cout << "test_funtion!" << std::endl;
}
int main()
{
std::thread t1(test_funtion);
try
{
for (int i = 0; i < 100; ++i)
{
std::cout << "main count i:" << i << std::endl;
}
}
catch (...)//捕获所有异常
{
t1.join();
throw;//抛出异常
}
t1.join();
return 0;
}
code3:
用对象初始化线程
#include
#include
class Test{
public:
void operator()(){//重载函数调用运算符
for (int j = 0; j < 100; ++j){
std::cout << "object test j:" << j<
输出杂乱无章。
#include
#include
#include
class Test{
public:
void operator()(const std::string& temp){//重载函数调用运算符
for (int j = 0; j < 100; ++j){
std::cout << temp +"object test j:" << j<
unsigned num_cpus = std::thread::hardware_concurrency();//获取cpu core个数
std::cout << "num_cpus " << num_cpus << std::endl;
t1.join();
return 0;
}
构造线程时,若参数为引用类型,需要使用std::ref,使用&是没有效果的。
code4:
数据互斥和竞争
#include
#include
void test_funtion(){
for (int i = 0; i < 100; ++i)
{
std::cout << "test_funtion count i:" << i << std::endl;
}
}
int main()
{
std::thread t1(test_funtion);
for (int i = 0; i < 100; ++i)
{
std::cout << "main count i:" << i << std::endl;
}
t1.join();
return 0;
}
输出很乱,因为t1线程和主线程都在使用cout资源会发生竞争。
#include
#include
#include
#include
std::mutex mu;
void print(std::string s, int i){
mu.lock();
std::cout << s <<"count i:"<< i << std::endl;
mu.unlock();
}
void test_funtion(){
for (int i = 0; i < 100; ++i)
{
print("test_funtion ", i);
}
}
int main()
{
std::thread t1(test_funtion);
for (int i = 0; i < 100; ++i)
{
print("main ", i);
}
t1.join();
return 0;
}
加锁后,可以看到,输出很有次序。cout的访问权限是通过mu来同步的,两个线程不会同时访问cout,只要两个线程都是通过print函数打印的,那么就不会出现上面次序混乱问题。这里面,若
std::cout << s <<"count i:"<< i << std::endl;
发生异常,那么mu就会一直锁住,故使用lock和unlock要小心。可以使用省事的方法:lock_guard。
void print(std::string s, int i){
std::lock_guard lg(mu);
//mu.lock();
std::cout << s <<"count i:"<< i << std::endl;
//mu.unlock();
}
只要lg构造时为mu上锁,析构时解锁,因为lg是局部变量,出大括号就析构了,所以即使有异常也会正常解锁。还有问题:cout是全局的,其他线程在不加锁的情况下就可以使用,mu保护cout还是力不从心的。
#include
#include
#include
#include
#include
std::mutex mu;
class LogFile{
public:
LogFile(){
f.open("log.txt");
}
void log(std::string s, int i){
std::lock_guard lg(m_mutex);
f << s << " count i:" << i << std::endl;
}
protected:
private:
std::mutex m_mutex;//保护f
std::ofstream f;
};
void test_funtion(LogFile& lf){
for (int i = 0; i < 100; ++i)
{
lf.log("test_funtion ", i);
}
}
int main()
{
LogFile logFile;
std::thread t1(test_funtion,std::ref(logFile));
for (int i = 0; i < 100; ++i)
{
logFile.log("main ", i);
}
t1.join();
return 0;
}
这样比code4有改善,要想使用f,必须使用LogFile类,在类里面f又是被m_mutex保护的,所以是线程安全的。