zoukankan      html  css  js  c++  java
  • C++线程

    本着能用STL就不用其他库的原则,本文以c++11的std::thread作为接口开发。【VS2010不支持C++11的线程,VS2013支持】

    根据我另一个帖子,线程函数只能是全局或者静态的。https://www.cnblogs.com/judes/p/5921104.html

    全局函数:只能访问全局变量,需要注意加互斥锁

    静态函数:只能访问静态变量

    项目中常常需要线程访问类中的成员,本文也重点实现这个功能,下面提供两种方式。使用多线程卖票举例:

    卖票头文件:

    #pragma once
    #include <thread>
    #include <mutex>
    #include <Windows.h>
    using namespace std;
    class SellTickts
    {
    public:
        void SellTickts::seller_1();
        void SellTickts::seller_2();
        int ticketNum = 10;
        mutex Mutex;
    public:
        SellTickts();
        virtual ~SellTickts();
        void sell();
        friend void sell(void* param);
    };

    卖票源文件:

    #include "stdafx.h"
    #include "SellTickts.h"
    
    
    SellTickts::SellTickts()
    {
    
    }
    
    
    SellTickts::~SellTickts()
    {
    
    }
    //函数名称:售票员1
    //函数作用:NULL
    //函数参数:NULL
    //函数返回值:NULL
    //函数信息:NULL
    //备注:NULL
    void SellTickts::seller_1()
    {
        for (;;)
        {
            Mutex.lock();
            if (ticketNum == 0)
            {
                break;
            }
            else
            {
    
                ticketNum--;
                printf("Seller_1 sold a ticket, remains %d.
    ", ticketNum);
                if (ticketNum == 0)
                {
                    printf("Tickets has been sold out.
    ");
                    break;
                }
            }        
            Mutex.unlock();
    Sleep(100); } }
    //函数名称:售票员2 //函数作用:NULL //函数参数:NULL //函数返回值:NULL //函数信息:NULL //备注:NULL void SellTickts::seller_2() { for (;;) { Mutex.lock(); if (ticketNum == 0) { break; } else { ticketNum--; printf("Seller_2 sold a ticket, remains %d. ", ticketNum); if (ticketNum == 0) { printf("Tickets has been sold out. "); break; } } Mutex.unlock();
    Sleep(100); } }
    //函数名称:类函数售票 //函数作用:NULL //函数参数:NULL //函数返回值:NULL //函数信息:NULL //备注:NULL void SellTickts::sell() { printf("class sell. "); thread thread1(&SellTickts::seller_1,this); thread thread2(&SellTickts::seller_2,this); thread1.detach(); thread2.detach(); } //函数名称:友元函数售票 //函数作用:NULL //函数参数:NULL //函数返回值:NULL //函数信息:NULL //备注:NULL void sell(void* param) { printf("friend sell. "); SellTickts* pThis = (SellTickts*)param; thread thread1(&SellTickts::seller_1, pThis); thread thread2(&SellTickts::seller_2, pThis); thread1.detach(); thread2.detach(); }

    main函数:

    // ControlTest.cpp : 定义控制台应用程序的入口点。
    //
    void test();
    #include "stdafx.h"
    #include "SellTickts.h"
    int _tmain(int argc, _TCHAR* argv[])
    {
        SellTickts sellTickets;
        sellTickets.sell();//类函数作为函数入口
    sell(
    &sellTickets);//友元函数作为函数入口

       thread t(test);//全局函数作为函数入口
    t.detach();

    thread t1(&SellTickets::seller_1,this);//类外部使用类函数作为函数入口
    t1.detach();
    ) while (true) { } return 0; }

    关于友元函数的分析:https://www.cnblogs.com/judes/p/8202602.html

    分析:

    1、类函数作为线程入口:thread thread1(&SellTickts::seller_1,this);

     第一个参数是函数的地址,这里有个概念:无论类都有多少个对象,这些对象所使用的类函数都是同一个地址,只有属性地址不同。

    class A{
           int temp;
           void fun();
    };

    无论A类有多少个对象,这些对象所调用fun函数时,都是用的同一个地址,这个地址在类A定义的时候就有了;但是每个对象中的属性test地址都是不同的。那么fun函数如何区分当前是被哪个对象调用的呢?fun函数有一个默认参数this,当对象声明时,就有了一个对应的this,谁调用fun,就把谁的this传进去,这样就没问题了。

    注意:如果想要类函数的地址,需要&类::函数,不能&对象::函数,这也是我刚开始不理解的地方。

    第二个参数时this,是因为类函数都有默认参数this,所以这里必须传递参数this进去,否则编译出错。

    2、友元函数作为线程入口:thread thread1(&SellTickts::seller_1, pThis);

     其实原理一样,不同的就是友元函数灭有默认this参数,需要程序员手动放入类指针。

    3、使用全局函数作为线程入口:thread t(test);

    不知道为啥这里不使用函数的地址

    ps:怎么获取线程id和程序id

    直接使用thread的get_id函数返回的是个thread类的内部类,不方便获取id,可以使用各平台自己的api

    #ifndef __linux__
    #include "windows.h"
    #else
    #include "unistd.h"
    #endif
    
    #ifndef __linux__
    printf("now pid is %d 
    ", GetCurrentProcessId());
    printf("now tid is %d 
    ", GetCurrentThreadId());
    
    #else
    printf("now pid is %d 
    ", getpid());
    printf("now tid is %d 
    ", gettid());
    #endif

    ps:关于多线程调用同一全局函数的理解【转:https://www.cnblogs.com/liangjf/p/9801496.html】

    每个线程有自己的堆栈空间,而全局函数是只读的,当多个线程使用这个函数的时候,应该会把函数功能复制一份到自己的堆栈,然后函数内部的变量都放在自己的空间内,这样就互不影响了。

    当然如果访问的是同一个全局变量,加互斥锁就可以了。

    ps:thread::detach和join的区别

    join()操作是在std::thread t(func)后“某个”合适的地方调用,其作用是回收对应创建的线程的资源,避免造成资源的泄露。detach()操作是在std::thread t(func)后马上调用,用于把被创建的线程与做创建动作的线程分离,分离的线程变为后台线程,其后,创建的线程的“死活”就与其做创建动作的线程无关,它的资源会被init进程回收。

    主要谈join:

    void test()
    {
    }
    
    bool do_other_things()
    {
    }
    
    int main()
    {
        std::thread t(test);
        int ret = do_other_things();
        if(ret == ERROR) {
            return -1;
        }
    
        t.join();
        return 0;
    }

    可能执行不到join

    解决:

    class mythread {
    private:
        std::thread &m_t;
    
    public:
        explicit mythread(std::thread &t):m_t(t){}
        ~mythread() {
            if(t.joinable()) {
                t.join()
            }
        }
    
        mythread(mythread const&) = delete;
        mythread& operate=(mythread const&) = delete;
    }
    
    void test()
    {
    }
    
    bool do_other_things()
    {
    }
    
    int main()
    {
        std::thread t(test);
        mythread q(t);
    
        if(do_other_things()) {
            return -1;
        }
    
        return 0;
    }

    ps:休眠

    #include <thread>
    #include <chrono>
    
    
    std::this_thread::sleep_for(std::chrono::milliseconds(50));
  • 相关阅读:
    Git—分支管理
    Git—推送代码至Github
    Git入门—创建项目
    Mysql单表查询
    Mysql数据的增删改查
    Mysql完整约束性
    C++继承中同名成员变量处理方法
    C++继承和组合中的构造函数和析构函数调用原则
    C++继承中的类型兼容原则
    C++中的继承
  • 原文地址:https://www.cnblogs.com/judes/p/10863290.html
Copyright © 2011-2022 走看看