c++线程

第一章:线程概念

#include <iostream>
#include <thread>

using namespace std;

int main()
{
    //并发,进程,线程的基本概念和概述
    //并发,进程,线程,必须掌握

    // 1.并发
    //  两个或者更多的任务(独立的活动)同时执行:一个程序同时执行多个独立任务
    //  计算机单核CPU 同一时刻只能执行一个任务:由操作系统调度,每秒钟进行多次的任务切换,时间片轮转。
    //并发假象(不是真正的并发);这种切换也叫做上下文切换,切换是有时间开销的,操作系统要保存你切换时的状态,执行进度等信息。
    //  一会儿切换回来要复原
    //  硬件发展,出现了多处理器计算机,用于服务器和高性能计算领域
    //  台式机:在一块芯片上有多核cpu:双核 4核,8核
    //  能够实现真正的并行执行多个任务(硬件开发),任务数量小于CPU核数
    //使用并发的原因:主要是同时干多个任务,提高性能

    // 2.可执行程序
    //    扩展名为.exe为可执行程序, Linux中 有X的就是可执行程序
    // 3.进程概念
    // Windows下双击.exe运行可执行, Linux下。/a
    // 可执行程序运行就是创建了进程。
    // 4,线程概念
    // 每个进程(执行起来的可执行程序),都有一个主线程,主线程是唯一的,一个进程只有一个主线程。
    // 当执行可执行程序,产生进程后,主线程会随着进程自动启动。
    // 运行程序,实际上是进程的主线程来执行。
    //线程:用来执行代码,代码的执行道路。

    //除了主线程外,可以自己写代码创建其他线程,走别的道路,去不同地方
    //每创建一个新线程,就可以在同一时刻,多干一个不同的事。

    //多线程(并发)不是越多越好,需要独立的堆空间(1M),线程切换要保存中间状态,
    //切换会耗费本该属于程序运行的时间。
    //总结线程:
    //线程是用来执行代码的
    //线程理解为代码的执行通路,一个新线程代表一条新的通路
    //创建的线程不建议超过200-300个,

    // 5学习心得
    //  开发多线程程序:实力的体现。一个是商用的必须需求
    //线程开发有一定的难度,实现代码复杂,理解上难一点。
    // C++线程会涉及很对概念,不用基于求成。

    //二、并发方法
    //  两个或者更多的任务(独立的活动)同时执行:一个程序同时执行多个独立任务
    // 实现并发:通过多个进程。
    //在单独的进程中,创建多个线程。自己写代码
    
    //1.多进程并发
    // 程序启动就是进程,
    // 进程通信(同一个电脑:文件,管道,消息队列,共享内存)
    // 不同电脑:SOCKET通信技术。
    //2.多线程并发:单个进程创建的多个线程。
     /*
    // 线程:每个线程都有自己独立的运行路径,但是一个进程中的所有线程共享地址空间。(共享内存)
    // 全局变量,指针,引用,都可以在线程中传递,使用多线程开销小于多进程。
    共享内存带来的问题,数据一致性的问题。
    //多进程并发和多线程可以混合使用,优先使用多线程手段。
    本章只讲多线程并发技术。
    和进程比,线程启动速度更快,更轻量级,开销更少,执行速度更快,
缺点:使用有难度,要小心处理,数据一致性问题。
//三。C++11新标准库
以前:windowns:CreateThread(),_beginthred()
linux:phread_create();
//临界区,互斥量。
    POSIX:跨平台,但是需要配置,用起来也不方便。
从C++11引入了多线程支持,意味着可移植(跨平台)。大大减少工作量
    */
    
    

    return 0;
}

第二章 :线程创建,启动

函数创建线程

#include <iostream>
#include <thread>

using namespace std;
void myprint()
{
    cout << "我的线程开始执行" << endl;
    cout << "我的线程执行完毕" << endl;
}
int main()
{
    /*
 //线程运行的开始和结束。
 //主线程从main()开始执行,我们从另一个函数开始执行。
 //当主线程执行完毕后,整个进程就执行完毕,如果子线程没有执行完毕,会强行终止
 //要让子线程报仇运行状态,让主线程一直保存运行。
 有例外
a.包含 #include <thread>
b.创建的初始函数 myprint
 */
    thread t(myprint);
    t.join();
    cout << "I Love China" << endl;
    return 0;
}

 类创建线程

#include <iostream>
#include <thread>

using namespace std;
void myprint()
{
    cout << "我的线程开始执行" << endl;
}
class TA {
public:
    int m;
    TA(int i)
        : m(i) {};

    ~TA() { cout << "析构函数调用了" << endl; };
    TA(const TA& ta)
        : m(ta.m)
    {
        cout << "拷贝构造函数调用了" << endl;
    };
    void operator()()
    {
        cout << "我的线程operator()开始执行了" << endl;
        cout << "m1的值是 = " << m << endl; //,因为程序可能已经销毁了,会产生不可预料的代码。

        cout << "我的线程operator()结束执行了" << endl;
    }
};
int main()
{
    /*
 //线程运行的开始和结束。
 //主线程从main()开始执行,我们从另一个函数开始执行。
 //当主线程执行完毕后,整个进程就执行完毕,如果子线程没有执行完毕,会强行终止
 //要让子线程报仇运行状态,让主线程一直保存运行。
 有例外
a.包含 #include <thread>
b.创建的初始函数 myprint
有两个线程在执行,可以同时做两个事情,一条线被堵住了,另一条线可以执行。
(1.1)thread 是类,用来创建线程
(1.2)join 加入/汇合,阻塞主线程,让主线程等待子线程执行完毕,主线程在继续执行。
如果主线程执行完毕,但子线程还没有执行完毕,写出来的代码是不稳定的,
需要主线程等待子线程执行完毕在退出。
(1.3)detach()分离 :传统需要等待子线程执行完毕,然后退出,
创建了多个子线程,让主线程等待子线程结束,在退出
一旦detach后,会被运行时库接管,,清理程序资源,(守护线程deamon)
(1.4)jsonable()返回是否可以join或者detach
 */
    // 可调用对象
    //    thread t(myprint); //创建了线程已经开始执行了。
    //    cout<<t.joinable()<<endl; //返回是否可以join和detach
    //    t.join();
    //    t.detach(); //一旦调用detach ,就不能再用join了,否则会有异常。
    //    cout << "主线程收尾,最终安全退出" << endl;
    //二、其他创建线程的方式
    int myi = 9;
    TA a(myi);
    thread t(a); // a是可调用对象
    t.detach();
    //    t.join();
    cout << "主线程收尾,最终安全退出" << endl;

    return 0;
}

 lambda创建线程

    // 三。用lambda表达式
    auto mylambda = []() {
        cout << "我的线程3开始执行了" << endl;
        cout << "我的线程3执行结束了" << endl;
    };
    thread t(mylambda);
    
    cout << "主线程收尾,最终安全退出" << endl;

第三章:线程传参传参

#include <iostream>
#include <thread>

using namespace std;
void myprint(const int i, const string& printf) //此处i不是真的引用,他和外部地址不一样,虽然安全,但是不建议使用,此处使用值传递,
//    void myprint(const int& i, char* printf) //此处i不是真的引用,他和外部地址不一样,虽然安全,但是不建议使用,此处使用值传递,

{
    cout << "i = " << i << "i address = " << &i << endl;
    cout << "printf = " << printf << "printf address = " << &printf << endl;
}
void test(int& i, char* printf)
{
    cout << "i = " << i << "i address = " << &i << endl;
    cout << "printf = " << printf << "printf address = " << printf << endl;
}
int main()
{
    /*

 //一、传递临时对象作为线程参数
(1.1)要避免的陷阱(解释1) 相对安全
(1.2)要避免的陷阱(解释2)

    */
    int muar = 1;
    int& m = muar;
    char array[] = "afda";
    cout << "m address = " << &m << endl;
    cout << "array address = " << &array << endl;

//    thread t(myprint, m, array); //但是array是什么时候转换为string的
    //事实上,array都被回收了(main函数执行完了),系统才用array转string的可能
    thread t(myprint,m,string(array)); //最终写法,这是一个可以保证在线程中用肯定有效的写法。
    
    t.detach();
    //    test(m, array);
    cout << "Finish!!!" << endl;
    return 0;
}

#include <iostream>
#include <thread>

using namespace std;
void myprint(int i, const string& printf) //此处i不是真的引用,他和外部地址不一样,虽然安全,但是不建议使用,此处使用值传递,
//    void myprint(const int& i, char* printf) //此处i不是真的引用,他和外部地址不一样,虽然安全,但是不建议使用,此处使用值传递,

{
    i = 100;

    cout << "i = " << i << "i address = " << &i << endl;
    cout << "printf = " << printf << "printf address = " << &printf << endl;
}
class A {

public:
    int m_i;
    A(int a)
        : m_i(a)
    {
        //        m_i = a;
        cout << "构造函数执行" << endl;
    };
    A(const A& a)
        : m_i(a.m_i)
    {
        //        m_i = a.m_i;
        cout << "拷贝构造函数执行";
    };
    ~A() { cout << "析构函数执行" << endl; };
};

void test(const int i, const A& a)
{
    cout << &a << endl;
    return;
}

int main()
{
    /*

 //一、传递临时对象作为线程参数
(1.1)要避免的陷阱(解释1) 相对安全
(1.2)要避免的陷阱(解释2)
(1.3总结)
如果是简单的类型用值传递,如果传递类对象,避免隐式类型转换。全部在创建线程这一行就构建出对象来,在函数中,用引用接受。
最终结论()
建议不使用detach,就不会存在对内存的非法引用问题。
    */
    int muar = 1;
    //    int& m = muar;
    int m2 = 2;

    thread t(test, muar, A(m2)); //类对象是最后调用的,隐式转为类,不会构造类,有问题。
    //                          使用显示转换会先进行转换。通过调用临时构造函数,在调用拷贝构造函数。
    //在创建线程的同时创建临时对象的方法传递参数可行。
    t.detach();
    cout << "Finish!!!" << endl;

    return 0;
}

第四章:创建多个线程

 

创建多个线程 

#include <iostream>
#include <mutex>
#include <thread>
#include <vector>

using namespace std;

void myThread(int num)
{
    cout << num << "myprint线程开始执行" << endl;

    cout << num << "myprint线程结束执行" << endl;
}
int main()
{
    //一.创建多个线程
    vector<thread> mythreads;
    for (int i = 0; i < 10; i++) {
        mythreads.push_back(thread(myThread, i)); //创建十个线程,并运行
    }
    for (auto iter = mythreads.begin(); iter != mythreads.end(); iter++) {
        iter->join(); //等待结果返回
    }
    cout << "finish " << endl;
    return 0;
}

数据共享问题

#include <iostream>
#include <list>
#include <mutex>
#include <thread>
#include <vector>
using namespace std;
vector<int> v = { 1, 2, 3 };

void myThread(int num)
{
    cout << num << "myprint线程开始执行" << endl;
    cout << this_thread::get_id() << "线程打印v的值" << v[0] << " " << v[1] << endl;
    cout << num << "myprint线程结束执行" << endl;
}

class A {
public:
    //把收到的消息传入队列
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 10000; ++i) {
            cout << "inMsgRecvQueue()<<插入数据" << i << endl;
            msgRecvQueue.push_back(i); //设置数字为收到的命令,
        }
    }
    //把数据读取出来
    void outRecvQueye()
    {
        for (int i = 0; i < 10000; ++i) {
            if (!msgRecvQueue.empty()) {
                //不为空
                int command = msgRecvQueue.front(); //从前开始取,不检查元素是否存在
                msgRecvQueue.pop_front(); //移除,但不返回
                //考虑处理

            } else {

                cout << "outRecvQueye()执行,但是元素为空" << i << endl;
            }
        }
        cout << "end" << endl;
    }

private:
    list<int> msgRecvQueue;
};
int main()
{

    //    (2.3)其他案例
    //    数据共享;火车买票
    //三.共享数据的保护代码
    //网络服务器,两个线程,一个收集命令,写入队列,另一个取出信息.
    //        list容器,频繁的按顺序插入和删除数据时效率高,vector容器随机的插入和删除数据效率高

    //用成员函数作为线程参数写线程
    A obj;
    thread recv(&A::inMsgRecvQueue, &obj);
    thread t(&A::outRecvQueye, &obj); //保证是同一个对象,保证线程用的是同一个对象
    t.join();

    return 0;
}

数据共享问题解决

 互斥量(mutex)|| std::lock_guard

#include <iostream>
#include <list>
#include <mutex>
#include <thread>
#include <vector>
using namespace std;

class A {
public:
    //把收到的消息传入队列
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 100000; ++i) {
            cout << "inMsgRecvQueue()<<插入数据" << i << endl;
            std::lock_guard<mutex> sbguard(mu); //使用lock_guard
            mu.lock();
            msgRecvQueue.push_back(i); //设置数字为收到的命令,
            mu.unlock();
        }
    }
    //把数据读取出来
    void outRecvQueye()
    {
        for (int i = 0; i < 100000; ++i) {
            if (!msgRecvQueue.empty()) {
                //不为空
                int command = msgRecvQueue.front(); //从前开始取,不检查元素是否存在
                msgRecvQueue.pop_front(); //移除,但不返回
                //考虑处理
                cout << "读取出来的数据是:" << command << endl;
            } else {

                cout << "outRecvQueye()执行,但是元素为空" << i << endl;
            }
        }
        cout << "end" << endl;
    }

private:
    list<int> msgRecvQueue;
    mutex mu; //创建了互斥量
};
int main()
{

    //    (2.3)其他案例
    //    数据共享;火车买票
    //三.共享数据的保护代码
    //网络服务器,两个线程,一个收集命令,写入队列,另一个取出信息.
    //        list容器,频繁的按顺序插入和删除数据时效率高,vector容器随机的插入和删除数据效率高
    //用成员函数作为线程参数写线程
    A obj;
    thread out_t(&A::outRecvQueye, &obj); //保证是同一个对象,保证线程用的是同一个对象
    thread recv_t(&A::inMsgRecvQueue, &obj);

    recv_t.join();
    out_t.join();

    //互斥量,保护共享数据mutex 多个线程尝试,只有一个可以成功
    //步骤:先lock操作共享数据,在unlock.两者需要成对使用.
    //为了防止大家出现忘记写unlock的情况,引入了一个std::lock_guard的类模板,替你unlock
    // lock_guard直接取代lock 和unlock,也就是说用了lock_guard再也不能用lock

    return 0;
}

死锁

#include <iostream>
#include <list>
#include <mutex>
#include <thread>
#include <vector>
using namespace std;

class A {
public:
    //把收到的消息传入队列
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 100; ++i) {
            cout << "inMsgRecvQueue()<<插入数据" << i << endl;
            //            std::lock_guard<mutex> sbguard(mu); //使用lock_guard
            //            mu.lock();
            //            mu.lock(); //再次加锁,出现死锁现象.
            mu.try_lock();
            mu.try_lock(); //可以使用try_locy ,如果枷锁则返会false
            
            msgRecvQueue.push_back(i); //设置数字为收到的命令,
            mu.unlock();
//            mu.unlock();
        }
    }
    //把数据读取出来
    void outRecvQueye()
    {
        for (int i = 0; i < 100; ++i) {
            if (!msgRecvQueue.empty()) {
                //不为空
                int command = msgRecvQueue.front(); //从前开始取,不检查元素是否存在
                msgRecvQueue.pop_front(); //移除,但不返回
                //考虑处理
                cout << "读取出来的数据是:" << command << endl;
            } else {

                cout << "outRecvQueye()执行,但是元素为空" << i << endl;
            }
        }
        cout << "end" << endl;
    }

private:
    list<int> msgRecvQueue;
    mutex mu; //创建了互斥量
};
int main()
{

    //    (2.3)其他案例
    //    数据共享;火车买票
    //三.共享数据的保护代码
    //网络服务器,两个线程,一个收集命令,写入队列,另一个取出信息.
    //        list容器,频繁的按顺序插入和删除数据时效率高,vector容器随机的插入和删除数据效率高
    //用成员函数作为线程参数写线程
    A obj;
    thread out_t(&A::outRecvQueye, &obj); //保证是同一个对象,保证线程用的是同一个对象
    thread recv_t(&A::inMsgRecvQueue, &obj);

    recv_t.join();
    out_t.join();

    //互斥量,保护共享数据mutex 多个线程尝试,只有一个可以成功
    //步骤:先lock操作共享数据,在unlock.两者需要成对使用.
    //为了防止大家出现忘记写unlock的情况,引入了一个std::lock_guard的类模板,替你unlock
    // lock_guard直接取代lock 和unlock,也就是说用了lock_guard再也不能用lock

    //死锁,当你有两把锁的时候,先锁后
    return 0;
}

以下内容为旧的

使用线程第一个代码

#include <iostream>
#include <thread>

using namespace std;

void ThreadMain()
{
    cout << "thread main id = " << this_thread::get_id() << endl;

    for (int i = 0; i < 11; i++) {
        this_thread::sleep_for(10ms); // 10 毫秒
//        this_thread::sleep_for(chrono::seconds(1)); // 1秒

    } //设置睡眠时间
    cout << "end thread id = " << this_thread::get_id() << endl;
}
int main()
{
    thread t(ThreadMain); //线程创建启动
    t.join(); //堵塞等待子线程退出
    //通过id 区分线程号
    cout << " main id = " << this_thread::get_id() << endl;
    //    cout << "thread main id = " << t.get_id() << endl;

    return 0;
}

线程分离

#include <iostream>
#include <thread>

using namespace std;
bool is_exis = false;
void ThreadMain()
{
    cout << "thread main id = " << this_thread::get_id() << endl;

    for (int i = 0; i < 11; i++) {
        if (!is_exis)
            break;
        //        this_thread::sleep_for(10ms); // 10 毫秒
        this_thread::sleep_for(chrono::seconds(1)); // 1秒

    } //设置睡眠时间
    cout << "end thread id = " << this_thread::get_id() << endl;
}
int main()
{
    {
        thread t(ThreadMain); //出错,thread对象被销毁,子线程还在运行
        t.detach(); //子线程与主线程分离 守护线程
        //坑:主线程退出后,子线程还在运行
    }
    thread th(ThreadMain);
        this_thread::sleep_for(chrono::seconds(1));
        is_exis = true;
    th.join();

    return 0;
}

线程创建的多种方式

传参

#include <iostream>
#include <thread>

using namespace std;

class Person {
public:
    int a = 10;
    Person() { cout << "creaet " << endl; }
    Person(const Person& p)
    {
        cout << "copy " << endl;
        this->a = p.a;
    };
    ~Person() { cout << "delete " << endl; }
};

void ThreadMain(int p1, float p2, string str, Person s)
{
    this_thread::sleep_for(100ms);

    cout << "p1 = " << p1 << endl;
    cout << "p2 = " << p2 << endl;
    cout << "str = " << str << endl;
    cout << "Person =" << s.a << endl;
}

int main()
{
    Person s;
    //所有的参数是复制传参
    thread th(ThreadMain, 101, 23.4, "hello", s); //参时后面是... 代表可以传递任意的参数。
    th.join(); //等待
    cout << "finish iA = " << s.a << endl;
    return 0;
}

线程安全问题

当我们用多个线程同时操作一个全局数据时,会发生线程安全,导致值不是我们预期的,可以通过互斥锁,读写锁,原子操作,条件变量,线程本地变量

#include <atomic>
#include <iostream>
#include <mutex>
#include <thread>

int sharedData = 0;
// std::mutex mutex1;
void incrementData()
{
    for (int i = 0; i < 100000; ++i) {

        sharedData++; // 竞态条件,多个线程同时对 sharedData 执行写操作
    }
}

int main()
{
    std::thread t1(incrementData);
    std::thread t2(incrementData);

    t1.join();
    t2.join();

    std::cout << "Final value of sharedData: " << sharedData << std::endl;
    return 0;
}

互斥锁

#include <atomic>
#include <iostream>
#include <mutex>
#include <thread>

int sharedData = 0;
 std::mutex mutex1;
void incrementData()
{
    for (int i = 0; i < 100000; ++i) {
        mutex1.lock();
        sharedData++; // 竞态条件,多个线程同时对 sharedData 执行写操作
        mutex1.unlock();    
    }
}

int main()
{
    std::thread t1(incrementData);
    std::thread t2(incrementData);

    t1.join();
    t2.join();

    std::cout << "Final value of sharedData: " << sharedData << std::endl;
    return 0;
}

原子操作

#include <atomic>
#include <iostream>
#include <mutex>
#include <thread>

// int sharedData = 0;
std::atomic<int> sharedData = 0; //创建原子类
//atomic_int  sharedData = 0; //创建原子类 使用内置的宏声明

using namespace std;
void incrementData()
{
    for (int i = 0; i < 100000; ++i) {
        sharedData++; // 竞态条件,多个线程同时对 sharedData 执行写操作
    }
}

int main()
{
    clock_t start = clock(); //获取开始时间

    std::thread t1(incrementData);
    std::thread t2(incrementData);

    t1.join();
    t2.join();

    std::cout << "Final value of sharedData: " << sharedData << std::endl;
    clock_t end = clock();
    cout << end - start << endl;

    return 0;
}

线程本地变量

#include <iostream>
#include <thread>

thread_local int g_counter = 0; // 线程本地变量

void incrementCounter()
{
    ++g_counter;
    std::cout << std::this_thread::get_id() << ": " << g_counter << std::endl;
}

int main()
{
    std::thread t1(incrementCounter);
    std::thread t2(incrementCounter);

    t1.join();
    t2.join();

    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值