C++–多线程

C++–多线程

并发:多个任务同时进行(一个程序同时执行多个独立的任务),可提高性能
进程可执行程序运行起来,操作系统分配资源的基本单位。
线程:每个进程都有个唯一主线程,主线程执行main函数,主线程生命周期和进程一致
线程可以理解为一条代码的执行道路。
多线程(并发):单个进程创建多个线程,一个进程所有线程共享地址空间(共享内存,开销更少,需考虑数据一致性问题)【每个线程都需要独立的堆栈空间(1M),线程之间切换需要保存中间状态】
多进程(并发):服务器进程之间的通信【同一服务器:管道、文件、消息队列、共享内存;
不同服务器:socket通信】

线程创建 std::thread

1.线程的开始和结束
// 主函数从main()开始执行,创建线程要从一个函数开始运行,函数运行结束
// 一般情况下主线程执行完毕,子线程还没有执行完毕,这些子线程会被操作系统强行终止
// 需要保持子线程运行状态的话,要让主线程一直保持运行
// 也存在例外情况,detach主线程不和子线程汇合,主线程先走一步了
#include <iostream>
#include <thread>

using namespace std;

// 创建线程从函数开始运行
void myPrint()
{
   
	cout << "我的线程开始执行了..." << endl;
	cout << "我的线程执行结束了..." << endl;
}

int main()
{
   
	// 创建了线程,线程执行起点myPrint,线程开始执行
	thread mytobj(myPrint);  // myPrint可调用对象

	// 阻塞主线程,让主线程等待子线程执行完毕,然后子线程和主线程汇合
	mytobj.join();
	
	cout << "Hello cpp!" << endl;

	return 0;
}
 detach
// 一旦detach,与这个主线程关联的thread对象就失去关联,子线程驻留后台运行
// 相当于被C++运行时库接管,当子线程执行完成,由运行时库负责清理线程相关资源(守护线程)
thread mytobj(myPrint);
mytobj.detach();
// joinable判断是否可以成功使用join或detach
if (mytobj.joinable())
{
   
	cout << "join可用" << endl;
}
// 	2.其他线程创建方式:类
class CTest
{
   
public:
	CTest(int& i) : m_i(i)
	{
   
		cout << "CTest构造函数被执行了" << endl;
	}
	CTest(const CTest& ct) : m_i(ct.m_i)
	{
   
		cout << "CTest拷贝构造函数被执行了" << endl;
	}
	~CTest()
	{
   
		cout << "~CTest析构函数被执行了" << endl;
	}

	void operator()()
	{
   
		cout << "m_i的值为:" << m_i << endl;
	}
private:
	int &m_i;
};
int myint = 10;
CTest ct(myint);
thread mytobj2(ct);
mytobj2.join();

// mytobj2.detach();  
// 容易产生不可预料的结果,传引用myint被主线程释放
// ct对象是被拷贝构造到了线程中,主线程ct对象销毁后,不存在问题,会析构两次
//  3.其他线程创建方式:lambda表达式
auto myLambda = [] {
   
	cout << "线程3开始执行了" << endl;
};
thread mytobj3(myLambda);
mytobj3.join();

线程参数

//  1.传递临时对象作为线程参数
// 陷阱1:传指针有问题
// 陷阱2:构造函数有问题
// 建议(detech):int简单类型使用值传递;如果传递对象,避免隐式转换,直接构建临时对象
void myPrint(const int& i, char* pbuf)
{
   
    cout << i << endl;  
    // i并不是mvar的引用,实际为值传递,那么即使主线程deteah,子线程仍然安全(不建议引用)
    cout << pbuf << endl;  
    // pbuf和mybuf指向同一地址,使用deteah绝对有问题
}

int main()
{
   
    int mvar = 1;  // shift+f9快速监视
    int& mvary = mvar;
    char mybuf[] = "this is a test";
    thread mytobj(myPrint, mvary, mybuf);

    mytobj.join();
    // mytobj.detach();
    
    cout << "Hello, Cpp" << endl;
    return 0;
}
//  防止mybuf指向同一地址,detach出现问题,使用string
//  直接转换成string,就可以保证稳定(deteah)
//  【存在mybuf被回收了,系统才把mybuf由char[]去转string问题】
void myPrint2(const int i, const string& pbuf)
{
   
    cout << i << endl;
    cout << pbuf << endl;
}

int main()
{
   
    int mvar = 1;  // shift+f9快速监视
    int& mvary = mvar;
    char mybuf[] = "this is a test";
    thread mytobj(myPrint2, mvary, string(mybuf));
    // 直接转换成string,就可以保证稳定(deteah)
	
    mytobj.join();
    // mytobj.detach();
    
    cout << "Hello, Cpp" << endl;
    return 0;
}
//  类测试
class A
{
   
public:
    mutable int m_i;
    A(int i) : m_i(i){
    cout << "A的构造函数执行了" << this << " thread_id = " << this_thread::get_id() << endl; }
    A(const A& a) : m_i(a.m_i) {
    cout << "A的拷贝构造函数执行了" << this << " thread_id = " << this_thread::get_id() << endl; }
    ~A() {
    cout << "A的析构函数执行了" << this << " thread_id = " << this_thread::get_id() << endl; }

    void threadWork(int num)
    {
   
        cout << "子线程threadWork执行了" << this << " thread_id = " << this_thread::get_id() << endl;
    }

    void operator()(int num)
    {
   
        cout << "子线程operator()执行了" << this << " thread_id = " << this_thread::get_id() << endl;
    }
};

void myPrint3(const int i, const A& pbuf)
{
     
    cout << &pbuf << endl;  // 打印地址 
}
int main()
{
   
    int mvar = 1;
    int mvar2 = 12;
    
	//thread mytobj(myPrint3, mvar, mvar2);  
	// 希望mvar2构造成A类型对象(隐式转换)
    // 很可能主线程结束了,构造函数还没来得及构造
    
	thread mytobj(myPrint3, mvar, A(mvar2));  
	// 构造、拷贝、析构都会执行,一定在主线程结束前构造完(安全)
	//流程:构造->拷贝构造->析构->析构
	
	cout << "主线程id是:" << this_thread::get_id() << endl;
	
    mytobj.join();
    // mytobj.detach();
    
    cout << "Hello, Cpp" << endl;
    return 0;
}
//  2.传递类对象、智能指针作为线程参数
void myPrint4(const A& pbuf)
{
   
    pbuf.m_i = 100;
    cout << "子线程myPrint4参数地址是" << &pbuf << " thread_id = " << this_thread::get_id()  << endl;  // 打印地址 
}

void myPrint5(A& pbuf)
{
   
    pbuf.m_i = 100;
    cout << "子线程myPrint5参数地址是" << &pbuf 
### 2024年山东CCPC大赛相关信息 #### 赛事时间安排 根据已知的信息,2024年的中国大学生程序设计竞赛(CCPC)全国邀请赛将在山东举办一次重要赛事。具体日期未明确提及,但可以推测该赛事通常会在上半年举行[^1]。 #### 题目解析与技巧分享 对于2024 CCPC全国邀请赛(山东)暨山东省大学生程序设计竞赛的部分题目进行了详细解析。以下是部分题目的特点总结: - **Problem A**: 这道题目可能涉及基础算法的应用,适合初学者练习逻辑思维能力。 - **Problem C (多彩的线段2)**: 此题需要注意二分查找中的右边界设定为 \(2 \times 10^{18}\),并建议在满足条件时提前返回结果以优化性能[^2]。 ```java // 示例代码片段展示如何实现二分法 public class BinarySearchExample { public static long binarySearch(long left, long right) { while (left <= right) { long mid = left + (right - left) / 2; if (check(mid)) { // 自定义 check 函数判断当前值是否符合条件 return mid; // 提前返回结果 } else if (...) { left = mid + 1; } else { right = mid - 1; } } return -1; // 如果无解则返回特定标志 } private static boolean check(long value) { // 实现具体的检查逻辑 return true; } } ``` - **其他题目**: 如 Problem F、I 和 K 的解答也提供了详细的分析方法,帮助参赛者更好地理解复杂数据结构和算法的设计思路。 #### 历史对比与难度评估 通过回顾往届比赛情况可以看出,像2022年CCPC威海赛区的比赛虽然整体思路上并不算特别困难,但由于某些题目(如 Problem C 和 J)涉及到复杂的编码过程,因此对选手的实际编程能力和耐心提出了较高要求[^3]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柴寺仓

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值