零基础学高性能服务器开发

7 篇文章 0 订阅
6 篇文章 0 订阅

第四节 面向对象编程和基于对象编程

4.1 面向对象编程封装多线程

注意:pthreadlinux系统的默认库, 需手动链接-线程库 -lpthread

示例代码:

CMakeList

cmake_minimum_required(VERSION 3.21)
project(test)

set(CMAKE_CXX_STANDARD 11)

add_executable(test main.cpp Thread.cpp)
target_link_libraries(test pthread)#只需要库的名称

Thread.h

#ifndef TEST_THREAD_H
#define TEST_THREAD_H
#include <pthread.h>

class Thread {
public:
    Thread();
    virtual ~Thread();
    void Start();
    void Join();
    void SetAutoDelete(bool autoDelete);
private:
    static void* ThreadRoutine(void *arg);//共享一个成员函数
    virtual void Run()=0;
    pthread_t mThreadId_;
    bool autoDelete_;
};


#endif //TEST_THREAD_H

Threap.cpp

#include <iostream>
#include "Thread.h"
Thread::Thread():autoDelete_(false) {
    std::cout <<"Thread ..." << std::endl;
}
Thread::~Thread() {
    std::cout << "~Thread ..." << std::endl;
}

void Thread::Start() {
    pthread_create(&mThreadId_, NULL, ThreadRoutine, this);//注意此处的this,当前对象的自身指针
}

void Thread::Join() {
    pthread_join(mThreadId_, NULL);
}
//这才是线程的执行体,而不是run
void* Thread::ThreadRoutine(void *arg) {
    //Run 不可以直接run函数,因为它是静态函数,所以需要转换类型
    Thread* thread = static_cast<Thread*>(arg);//此处把this指针转换成基类指针,也就是把派生类指针转换成基类指针,虚函数的多态,并且具有回调功能
    thread->Run();//抽象基类调用自己的抽象函数
    if(thread->autoDelete_){
        delete thread;
        return NULL;
    }

}
void Thread::SetAutoDelete(bool autoDelete) {
    autoDelete_ = autoDelete;
}

Main.cpp

#include <iostream>
#include <unistd.h>
#include "Thread.h"

class TestThread:public Thread{
public:
    TestThread(int count):mCount_(count)
    {
        std::cout << "TestThread..." << std::endl;
    };
    ~TestThread(){
        std::cout << "~TestThread..." << std::endl;
    };

    void Run(){
        while (mCount_--){
            std::cout << "this is a test ..." << std::endl;
            sleep(1);
        }
    }

private:
    int mCount_;
};


int main() {
    /**@brief  (1)线程对象的生命周期和线程的生命周期是不一样的;
     *         (2)实现线程池,线程对象能够自动销毁;所以需要用动态的方式创建对象;所以要记住,多线程创建对象,一定要采用动态的方式
     * ***/
//    TestThread t(5);
//    t.Start();//主线程已经结束,子线程还没开始,所以需要加入等待函数,
//    t.Join();
    TestThread* t2 = new TestThread(5);
    t2->SetAutoDelete(true);
    t2->Start();
    t2->Join();
    for (; ; )
        pause();
    return 0;
}

4.1.1 pthread_create()函数

具体参考博客:https://blog.csdn.net/wushuomin/article/details/80051295

pthread_create是(Unix、Linux、Mac OS X)等创建线程的函数。它的功能是创建线程(实际上就是确定调用该线程函数的入口点),在线程创建以后,就开始运行相关的线程函数。pthread_create的返回值表示成功,返回0;表示出错,返回表示-1。
函数原型:

#include <pthread.h>
int pthread_create(
                 pthread_t *restrict tidp,   //新创建的线程ID指向的内存单元。
                 const pthread_attr_t *restrict attr,  //线程属性,默认为NULL
                 void *(*start_rtn)(void *), //新创建的线程从start_rtn函数的地址开始运行
                 void *restrict arg //默认为NULL。若上述函数需要参数,将参数放入结构中并将地址作为arg传入。
                  );

(1)传参数需要注意的问题:

避免直接在传递的参数中传递发生改变的量,否则会导致结果不可测。重新申请一块内存,存入需要传递的参数,再将这个地址作为arg传入。

(2)内存泄漏的问题

在默认情况下通过pthread_create函数创建的线程是非分离属性的,由pthread_create函数的第二个参数决定,在非分离的情况下,当一个线程结束的时候,它所占用的系统资源并没有完全真正的释放,也没有真正终止

只有在pthread_join函数返回时,该线程才会释放自己的资源。或者是设置在分离属性的情况下,一个线程结束会立即释放它所占用的资源。

示例代码:

#include <stdio.h>
#include <unistd.h>   //调用 sleep() 函数
#include <pthread.h>  //调用 pthread_create() 函数

void *ThreadFun(void *arg)
{
    if (arg == NULL) {
        printf("arg is NULL\n");
    }
    else {
        printf("%s\n", (char*)arg);
    }
    return NULL;
}

int main()
{
    int res;
    char * url = "http://c.biancheng.net";
    //定义两个表示线程的变量(标识符)
    pthread_t myThread1,myThread2;
    //创建 myThread1 线程
    res = pthread_create(&myThread1, NULL, ThreadFun, NULL);
    if (res != 0) {
        printf("线程创建失败");
        return 0;
    }
    sleep(5);  //令主线程等到 myThread1 线程执行完成
    
    //创建 myThread2 线程
    res = pthread_create(&myThread2, NULL, ThreadFun,(void*)url);
    if (res != 0) {
        printf("线程创建失败");
        return 0;
    }
    sleep(5); // 令主线程等到 mythread2 线程执行完成
    return 0;
}
4.1.2 pthread_join()函数

具体参考博客:https://blog.csdn.net/fanyun_01/article/details/107647111

函数原型:

int pthread_join(pthread_t thread, void **retval);

参数说明args:

pthread_t thread: 被连接线程的线程号

void **retval : 指向一个指向被连接线程的返回码的指针的指针

return:

线程连接的状态,0是成功,非0是失败

当调用 pthread_join() 时,当前线程会处于阻塞状态,直到被调用的线程结束后,当前线程才会重新开始执行。pthread_join() 函数返回后,被调用线程才算真正意义上的结束,它的内存空间也会被释放(如果被调用线程是非分离的)。这里有三点需要注意:

被释放的内存空间仅仅是系统空间,你必须手动清除程序分配的空间,比如 malloc() 分配的空间。

一个线程只能被一个线程所连接。
被连接的线程必须是非分离的,否则连接会出错。

所以可以看出pthread_join()有两种作用:

 用于等待其他线程结束:当调用 pthread_join() 时,当前线程会处于阻塞状态,直到被调用的线程结束后,当前线程才会重新开始执行。

  对线程的资源进行回收:如果一个线程是非分离的(默认情况下创建的线程都是非分离)并且没有对该线程使用。pthread_join() 的话,该线程结束后并不会释放其内存空间,这会导致该线程变成了“僵尸线程”。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值