• 基本概念

    • 并发
    • 可执行程序、进程、线程
    • 学习心得
  • 并发的实现方法
    • 多进程并发
    • 多线程并发
    • 总结
  • C++标准库

基本概念

(并发、进程、线程)
区分C++初级编程和中高级编程

并发

两个或者更多的任务同时进行:一个程序同时执行多个任务,这些任务之间是相互独立的,以往的计算机只有单核CPU的时候,某个时刻只能执行一个任务。

微观上串行,宏观上并行

1.单核CPU操作系统调度,每秒钟进行任务切换,看起来好像是多任务同时进行,实际上通过切换而来。不同任务之间需要的资源不一样,在切换的时候需要保存一些变量,重新切换回来的时候需要恢复一些变量值(有时间开销、空间开销),任务越多开销越大--上下文切换。

2.多核CPU(多处理器计算机)、服务器和高性能计算领域。或者个人PC多核CPU,真正实现多任务同时进行,任务数少于核数,真正的并行执行。

使用并发的目的:同时完成多个任务,提高性能。

可执行程序、进程和线程

系统中的文件,比如window下的exe文件,或者linxu下的ls la, rwx,有执行权限的文件--可执行的程序。

进程是资源分配的基本单位,一般一个可执行程是一个进程。

1)程序创建之后,就是一个进程。

2)每个进程都有一个主线程,主线程是唯一。

实际上运行程序的时候是进程主线程执行 main函数中的代码,main函数执行完毕,主线程关闭,主线程的生命周期和进程一致。

实际上,执行代码的就是线程==>(操作系统)线程是资源调度的基本单位。“代码的执行道路”。

主线程之外,可以通过自己写代码创建其他线程,其他线程运行的别的程序。

注意:线程并不是越多越好,每个线程都需要一个物理空间,并且进程的上下文切换是有代价,要保存很多中间状态,线程过多,大部分时间都在切换线程上,会耗费本该属于程序运行的时间,如果线程多到一定程度的时候,程序的运行效率不高。

实际环境中,需要多个线程的情形:

玩家1: 充值

玩家2: 抽卡

玩家1用--充值线程-->改数据库,玩家2-->抽卡线程-->改数据库。

线程处理当前的任务之后才进行下一个任务。。。线程给客户端提供服务的过程。

总结

1.线程用来执行代码。

2.线程一条代码的执行通路,一个新线程代表一个新的通路。

3.进程自动包含一个主线程。主线程随着进程启动,通过编码创建其他多个线程:非主线程,但是创建的数量最大不建议超过200~300个,上下文切换耗费时间,实际项目中调优,有时候上下文切换占有太多没有意义的时间。

4.主线程自动启动,一个进程中至少一个线程--主线程。

5.多线程程序--不同线程提供不同的服务,运行效率高,到底有多高难以量化。实际项目中体会和优化。

并发的实现方法

多进程

软件打开后就是一个进程。

账号服务器和游戏逻辑服务器交互:通过端口交换数据。

不同电脑上的socket服务器

多线程

单个进程中,创建了多个线程,每个线程有自己独立的运行路径,但是一个进程中的所有线程共享地址空间(共享内存)。全局变量,指针,引用,都可以在线程之间传递,所以:使用多线程的开销远远小于多进程。

C++标准库

thread

class thread;

Thread

Class to represent individual threads of execution.

A thread of execution is a sequence of instructions that can be executed concurrently with other such sequences in multithreading environments,
while sharing a same address space.

  An initialized thread object represents an active thread of execution; Such a thread object is joinable, and has a unique thread id.

  A default-constructed (non-initialized) thread object is not joinable, and its thread id is common for all non-joinable threads.

   A joinable thread becomes not joinable if moved from, or if either join or detach are called on them.

例子:

执行函数:

void my_print()
{
cout << "my thread start\n";
for (int i = 0; i < 1000; ++i);
cout << "my thread end\n" << endl;
}

 在主函数中方写调用函数的操作

#include <thread>
#include <iostream> using namespace std;
int main()
{
//如果要保持自己创建的代码创建的线程,主线程保持运行。有例外
std::cout << "Hello World!\n";
//a)包含一个头文件
//b)创建一个函数
//c)main中开始写代码
thread myThread1(my_print);
thread myThread2(my_print);
myThread1.join();
myThread2.join();
cout << "Main Thread" << endl;
return 0;
}

  

join()

void join();
Join thread

The function returns when the thread execution has completed.

This synchronizes the moment this function returns with the completion of all the operations in the thread: This blocks the execution of the thread that calls this function until the function called on construction returns (if it hasn't yet).

After a call to this function, the thread object becomes non-joinable and can be destroyed safely.

join表示阻塞主线程,让主线程等待子线程执行完毕,然后线程再开始往下走。

主线程往下走,当遇到myThread1.join()的时候主线程等待子线程myThread1,当子线程执行完毕,流程汇合,主线程继续往下走。

1)阻塞主线程并等待子线程执行完毕。

2)如果子线程未执行完毕,主线程结束,程序不稳定!!!

detach()

detach(): 主线程不等待子线程。传统多线程创建了很多子线程,让主线程逐个等待子线程结束,这种编程方法不太好,所以引入了detach()。主线程不和子线程汇合,子线程跑到系统后台,相当于被C++运行时刻接管,当这个子线程执行完毕之后,由运行时库负责清理该进程相关的资源。

void detach();
Detach thread

Detaches the thread represented by the object from the calling thread, allowing them to execute independently from each other.

Both threads continue without blocking nor synchronizing in any way. Note that when either one ends execution, its resources are released.

After a call to this function, the thread object becomes non-joinable and can be destroyed safely.

Linux:驻留后台==>守护线程,不由当前线程执行

    std::cout << "Hello World!\n";
//a)包含一个头文件
//b)创建一个函数
//c)main中开始写代码
thread myThread1(my_print,'1');
thread myThread2(my_print,'2');
myThread1.detach();
myThread2.detach();
cout << "Main Thread" << endl;

 ???放到后台执行了吗?????在主线程最后加一个while(1);可以看到完整的结果

1) detach() 是将函数放到后台执行

2) 主线程退出,子线程也会退出

3) detach() 线程使线程失去我们自己的控制

所以,通常情况下主线程等子线程。一旦detach()之后不能再用join,最常用的是要控制线程的生命周期。

joinable()

判断是否可以成功使用join()或者detach(),复杂的代码的时候,需要用到。

bool joinable() const noexcept;
Check if joinable

Returns whether the thread object is joinable.

thread object is joinable if it represents a thread of execution.

thread object is not joinable in any of these cases:

其他创建线程的方法

通过类构造一个可调用对象

class MyThread {
public:
void operator()() //不能带参数
{
cout << "my thread start\n"; cout << "my thread end\n" << endl;
}
};

  

//类对象的写法

	MyThread obj;
thread myThread(obj);
myThread.join();

 如果要传参用如下的写法构造

class MyThread {
public:
int& m_i;
MyThread(int& i) :m_i(i) {}
void operator()()
{
cout << "my thread start\n"; cout << "m_i的值为" << m_i << endl; cout << "my thread end\n" << endl;
}
};

  注意这个例子:如果用detach()后台执行线程obj,主线程和MyThread 的对象obj的运行路径分开,但是参数m_i是主线程按照引用的方式传给obj的,所以会产生如下情况:如果主线程运行结束,会释放变量,那么最后结果中,分离的线程obj会调用一个不存在的变量,运行错误。

主线程执行完之后,构造的对象也会被销毁?为什么使用类没有问题?一旦调用了detach(),那么主线程执行结束了,可调用对象也能执行。因为用了detach()之后把对象复制到线程中,主线程销毁后,复制的对象依然存在,所以没有引用,或者指向这些局部变量的指针,就不会产生问题。===>对象是被复制到线程中去。可以写一个拷贝构造函数,看一下结果。

参考文献

https://study.163.com/course/courseLearn.htm?courseId=1006067356&from=study#/learn/video?lessonId=1053452413&courseId=1006067356

http://www.cplusplus.com/reference/thread/thread/

C++并发与多线程学习笔记--基本概念和实现的更多相关文章

  1. C++并发与多线程学习笔记--互斥量、用法、死锁概念

    互斥量(mutex)的基本概念 互斥量的用法 lock(), unlock() std::lock_guard类模板 死锁 死锁演示 死锁的一般解决方案 std::lock()函数模板 std::lo ...

  2. C++并发与多线程学习笔记--future成员函数、shared_future、atomic

    std::future的其他成员函数 std::shared_future 原子操作.概念.基本用法 多线程主要是为了执行某个函数,本文的函数的例子,采用如下写法 int mythread() { c ...

  3. C++并发与多线程学习笔记--参数传递详解

    传递临时对象 陷阱 总结 临时对象作为线程参数 线程id的概念 临时对象构造时的抓捕 成员函数指针做线程函数 传递临时对象作为线程参数 创建的工作线程不止一个,线程根据编号来确定工作内容.每个线程都需 ...

  4. C++并发与多线程学习笔记--atomic

    std::atomic std::async std::atomic 一般atomic原子操作,针对++,--,+=,^=是支持的,其他结果可能不支持. 注意 std::atomic<int&g ...

  5. C++并发与多线程学习笔记--单例设计模式、共享数据分析

    设计模式 共享数据分析 call_once 设计模式 开发程序中的一些特殊写法,这些写法和常规写法不一样,但是程序灵活,维护起来方便,别人接管起来,阅读代码的时候都会很痛苦.用设计模式理念写出来的代码 ...

  6. C++并发与多线程学习笔记--多线程数据共享问题

    创建和等待多个线程 数据和共享问题分析 只读的数据 有读有写 其他案例 共享数据的保护案例代码 创建和等待多个线程 服务端后台开发就需要多个线程执行不同的任务.不同的线程执行不同任务,并返回执行结果. ...

  7. C++并发与多线程学习笔记--async、future、packaged_task、promise

    async future packaged_task promise async std:async 是个函数,用来启动一个异步任务,启动起来一个异步任务之后,返回一个std::futre对象,启动一 ...

  8. C++并发与多线程学习笔记--线程之间调度

    condition_variable wait() notify_one notify_all condition_variable 条件变量的实际用途: 比如有两个线程A和B,在线程A中等待一个条件 ...

  9. C++并发与多线程学习笔记--unique_lock详解

    unique_lock 取代lock_quard unique_lock 的第二个参数 std::adopt_lock std::try_to_lock std::defer_lock unique_ ...

随机推荐

  1. App Store Connect

    App Store Connect https://developer.apple.com/support/app-store-connect/ https://developer.apple.com ...

  2. Android Studio 4.x

    Android Studio 4.x https://developer.android.com/studio https://d.android.com/r/studio-ui/whats-new- ...

  3. CSS transition & shorthand property order

    CSS transition & shorthand property order shorthand property https://developer.mozilla.org/en-US ...

  4. redux & connect

    redux & connect import React, { Component, // useState, // useEffect, } from 'react'; import { b ...

  5. zsh all in one

    zsh all in one zsh https://ohmyz.sh/ # install $ sh -c "$(curl -fsSL https://raw.github.com/ohm ...

  6. Flutter: provider 使用小部件的小部件构建的依赖注入系统

    文档 dependencies: provider: import 'package:dart_printf/dart_printf.dart'; import 'package:flutter/ma ...

  7. 数据库分表自增ID问题

    .................................................................................................... ...

  8. java中是否存在i+1<i?

    存在! 首先我们知道int的取值范围是: -2147483648~2147483647,最高位为符号位 2147483647的二进制为:01111111 11111111 11111111 11111 ...

  9. 手把手教你Spring Boot整合Mybatis Plus 代码生成器

    一.在pom.xml中添加所需依赖 <!-- MyBatis-Plus代码生成器--> <dependency> <groupId>com.baomidou< ...

  10. 搭载华为麒麟9000的Mate X2:秒售罄,一机难求

    本文首发 | 公众号:lunvey 昨日10点,搭载了麒麟9000芯片的华为Mate X2正式开售,定价17999,对于手机来说,价格实在是高昂. 虽然价格高昂,但是一分钱一分货,对于想抢先体验的网友 ...