首页 > 编程语言 >C++多线程

C++多线程

时间:2024-01-18 16:01:43浏览次数:43  
标签:函数 thread void C++ 线程 th 多线程 cout

C++ 多线程的语法以及使用

1. 线程的创建

首先创建一个多线程入口函数threadmain ,threadmain函数体中完成子线程所要做的事。
接着在主函数中创建线程对象th,调用构造函数,并传递一个函数指针作为入口函数:thread th(treadmain); 入口函数为thread 构造函数的参数。
之后在主线程中调用th.join()函数阻塞等待子线程结束,还可以使用this_thread::get_id()查看主线程和子线程分别对应的线程号。
代码:

#include<thread>

#include<iostream>

using namespace std;
void ThreadMain() {
	cout << "begin sub thread id" <<this_thread::get_id()<< endl;

	for (int i = 0; i < 10; i++) {
		this_thread::sleep_for(100ms);
		cout << "sub thread i   " << i << endl;
	}
	cout << "end sub thread id" << this_thread::get_id() << endl;
}
int main(int arg,char* argv[]) {
	
	thread th(ThreadMain);
	cout << "main thread id" << this_thread::get_id() << endl;

	cout << "begin wait thread id" << this_thread::get_id() << endl;
	th.join();
	cout << "end wait thread id" << this_thread::get_id() << endl;
	return 0;
}

以上代码实现了简单的多线程的调用及实现

2. std::thread 对象的生命周期,以及线程的等待分离

  1. 不对线程对象进行维护时
{
    thread th(thread);
}

此时程序将会报错,错误有多个原因,第一:主线程先一步退出;第二:子线程对象被销毁,而子线程还在运行。
2. 主线程阻塞等待子线程退出

	{
		thread th(ThreadMain);
		th.join();            //主进程阻塞     等待子进程结束
	}

主线程什么也做不了了,阻塞等待子线程结束。我们希望主线程与子线程同时运行,同时不想维护线程对象,这是我们可以使用 detach
3. 主线程和子线程分离

{
    thread th(threadmain);
    th.detach();
}

子线程与主线程分离,子线程成为守护进程[1],可能出现的问题:当主线程退出后,子线程不一定会退出,当子线程需要访问主线程的变量时,会报错。我们可以使子线程全部使用自身命名空间的变量来避免这种问题。但是这样一般会很麻烦。这是我们可以在主线程退出时,通知子线程结束
4. 设置变量通知子进程

bool is_exit=false;
  void ThreadMain() 
  cout << "begin sub thread id" << this_thread::get_id() << endl;

  for (int i = 0; i < 10; i++) {
  	if (is_exit) break;
  	this_thread::sleep_for(10ms);
  	cout << "sub thread i   " << i << endl;
  }
  cout << "end sub thread id" << this_thread::get_id() << endl;

  	int main(){
    {  thread th(ThreadMain);
  	this_thread::sleep_for(100ms);    //使用变量通知子线程退出
  	is_exit=true;
  	th.join();                        //主线程阻塞,等待子线程退出
  }
      }

入口函数中使用循环来模拟子线程长时间工作,设置了一个全局变量is_exit 用来通知,当主线程完成工作后,修改is_exit使子线程结束,并调用join() 回收子线程。

3.全局函数作为入口函数

如何传递参数

thread th(threadmain)构造函数基于模板函数,可以传递任意类型,当值传递时,调用构造函数时会进行一次拷贝,调用回调函数时会进行第二次拷贝。

#include<thread>

#include<iostream>

using namespace std;

class pase 
{
public:
  pase() {
  	cout << "created" << endl;
  }
  ~pase() {
  	cout << "droped" << endl;
  }
  pase(const pase& i) {
  	name = i.name;
  	cout << "copied" << endl;
  }
  void setname(string p) {
  	name = p;
  }
  string getname() {
  	return name;
  }
private:
  string name;
  }
 void Thread(int p1,float p2,string p3,pase p4) {
  cout << p1 << " " << p2 << " " << p3 << " " << p4.getname() << endl;
}
int main(int arc, char* argv[]) {
  	thread th;
  {
  	string t = "time";
  	pase p;
  	p.setname(t);
  	//所有参数进行复制
  	th=thread (Thread, 100, 100.0,"string",p );
  	
  }
  th.join();
}

上述代码的结果如下
Alt text
我们可以看到pase对象进行了两次拷贝,三次析构
如果我们传递指针参数时,则只会在pase对象销毁时进行一次析构

	void thread_ptr(pase* p) {
	this_thread::sleep_for(100ms);
	cout << "thread_main   " << p->getname() << endl;
}
    thread th;
	{
		pase p;
		p.setname("john");
		th=thread (thread_ptr, &p);
		
	}
	th.join();

结果如下:
图片
使用指针传参时会带来一个问题:当指针所指向的空间销毁后,子进程可能还会用到这个指针。去访问已经销毁的空间。

下面是使用引用

void thread_ref(pase & p) {
	this_thread::sleep_for(100ms);
	cout << "thread_main   " << p.getname() << endl;
}

{
		pase p;
		p.setname("john");
		thread th(thread_ref, ref(p));
		th.detach();
	}

ref(p)标识为传引用,防止歧义
指针与引用类似

成员函数作为线程入口函数

成员函数如何作为线程入口函数

创建类 mythread 在类中创建成员函数来作为入口,创建线程对象,调用构造函数时,将成员函数指针以及对象的地址(this指针)传入
代码如下:

class mythread {
public:
	void main(){
		cout << "mythread name" << name << endl;
	}
private:
	string name="john";
	int id = 001;
};
int main(){
mythread myth;
	// 参数为成员函数指针,以及this指针(对象地址)
	thread th(&mythread::main, &myth);
	th.join();
}

如何封装成员函数

我们可以创建一个基类,基类中包括了如何创建,终止,回收进程的函数,可以帮助我们更好的维护进程。当我们需要使用多线程完成任务时,可以构造一个子类来继承该基类,同时在子类中重写虚函数,来作为线程的入口函数。
代码如下;

class xthread {
public:
	void start() {
		th_ = thread(&xthread::main, this);
	}
	virtual void main() =0;

	void wait() {
		if (th_.joinable())
			th_.join();
	}

	void stop() {
		exit = true;
		wait();
	}
	bool is_exit() {
		return exit;
	}
private:
	thread th_;
	bool exit = false;
};

class testthread :public xthread {
public:
	void main()  override//确保虚函数被重写{
		cout << "textthread "<<name << endl;
		cout << "textthread  begin"  << endl;
		while (!is_exit()) {
			this_thread::sleep_for(200ms);
			cout << "*"<<flush;
		}
		cout << "textthread   end"  << endl;
	}
	string name;
};
int main() {
	testthread testth;
	testth.name = "lili";
	testth.start();
	this_thread::sleep_for(2s);
	testth.stop();
}

我们在基类中创建了创建进程的start()函数,终止进程的stop() 和wait()函数。在子类中重写了main函数作为线程入口。

lambda表达式作为线程入口函数

lambda表达式可以被称为匿名函数或临时函数,可以做一些简单的操作或运算。
lambda函数基本格式为[捕捉列表](参数)->返回值类型 函数体{}
首先演示lambda表达式作为普通函数时的用法:

int main(){
    thread th([](){cout<<"lambda表达式";});
}

演示 lambda表达式作为成员函数

class test{
public:
void start{
    thread th([this](){cout<<"testname"<<name;});

}
private:
string name;

}

  1. 守护进程(守护进程(daemon process)是在后台运行的一种特殊类型的进程,通常独立于控制终端,并在系统启动时启动。守护进程通常在后台执行任务,不与用户直接交互,且会在系统运行时一直存在。它们常用于执行系统级任务、服务和周期性的工作. ↩︎

标签:函数,thread,void,C++,线程,th,多线程,cout
From: https://www.cnblogs.com/weakcore/p/17972660

相关文章

  • 20. 有效的括号C++
    括号匹配用栈是解决是最简单那的。遇到左括号就入栈。遇到右括号就出栈,然后看是否匹配。这里再用一个map把括号数字化会更简单。classSolution{public:boolisValid(strings){map<char,int>m={{'(',1},{')',-1},{'{',2},{'}',-......
  • KY109 Zero-complexity TranspositionC++
    h很简单的题目,不管是用数组还是用栈都非常简单。#include<iostream>#include<stack>usingnamespacestd;intmain(){intn;while(cin>>n){stack<long>s;while(n!=0){inttem;cin>>tem;......
  • C++内存分配揭秘:new操作符::operator new和Placement new的区别
     在C++中,new 操作符、::operatornew 和placementnew是用于动态内存分配的工具,但它们有不同的用法和行为。以下是它们的区别和用法的详细实例:1.new操作符new 操作符用于在堆上动态分配内存,并调用对象的构造函数初始化对象。#include<iostream>classMyClass{p......
  • Qt/C++自定义界面大全/20套精美皮肤/26套精美UI界面/一键换肤/自定义颜色/各种导航界
    一、前言这个系列对应自定义控件大全,一个专注于控件的编写,一个专注于UI界面的编写,程序员有两大软肋,一个是忌讳别人说自己的程序很烂很多bug,一个就是不擅长UI,基本上配色就直接rgb,对于第一点,只要放松心态,直面自己的不足,不断改进,才能问鼎武林至尊。至于第二点,因为程序员擅长的是逻辑......
  • C++继承顺序
    派生类可以访问基类中所有的非私有成员。因此基类成员如果不想被派生类的成员函数访问,则应在基类中声明为private。我们可以根据访问权限总结出不同的访问类型,如下所示:访问publicprotectedprivate同一个类yesyesyes派生类yesyesno外部的类yesnono......
  • 猫狗收容所 C++
    #include<iostream>#include<queue>usingnamespacestd;voiddelect(queue<int>*q,intx){if(q->empty())return;for(inti=0;i<q->size();i++){intt=q->front();q->pop();if(t!=x)q->push......
  • C++开发基础
    软件开发基础2024-01-0820:13星期一博客内容来自相关书籍和网站内容总结,仅供个人参考使用:笔者@东北大学StuBoo使用目录快速转到面试问题汇总、常见算法问题1.C++语言基础1.1C++语言特性概览面向对象编程(OOP):C++支持面向对象编程,包括封装、继承和多态。通过类和对象,可......
  • c++开发面试Q&A
    面试问题Q&A现阶段问题还没有归类,等待更新C++语言基础QC和C++两者优缺点,适合情况C语言特点:优点:简洁、高效、可移植性强、面向过程、底层控制、资源利用高。适用情况:嵌入式系统、操作系统、编译器、硬件驱动开发、对性能要求高的系统。C++语言特点:优点:面向对象、支持......
  • 27. 多线程技术
    一、多线程技术  QThread类是PySide中的核心线程类,要实现一个线程,需要创建QThread类的有一个子类,并且实现其run()方法。  线程也有自己自己的生命周期,其中包含5种状态,分别为:新建状态、就绪状态、运行状态、阻塞状态**和死亡状态。新建状态就是线程被创建时的状......
  • 3254:约瑟夫问题No.2C++
    \这题思路还是挺多的。如果用数学的角度考虑。知道了n,p,m自然就知道下一个要出队的人的编号。然后一个个输出就行了。还可以用循环链表做。还可以用队列。出队在入队。#include<iostream>#include<queue>usingnamespacestd;intmain(){intn,p,m;while(......