如何判断一个类是否是线程安全(可重入)的?

本文通过一个具体的类实现示例,探讨了线程安全的类设计原则。介绍了如何使用互斥锁确保多线程环境下类的方法调用不会引发数据竞争,从而保障线程安全。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

答案:若一个类对外提供的接口的是原子的,或者说多线程编程时不用对该类的接口考虑同步问题,那么该类就是线程安全的类。

栗子:

#include <iostream>
#include <string.h>
#include <atomic>
#include <pthread.h>
#include <unistd.h>

class Array
{
public:
    Array()
    {
        memset(data_, 0, sizeof(int) * 10);
        size = 0;
    }

public:
    void push_back(const int &data)
    {
        //pthread_mutex_lock(&mtx_);
        data_[size] = data;
        usleep(10);
        size++;
        //pthread_mutex_unlock(&mtx_);
    }
    void print()
    {
        for (size_t i = 0; i < 30; i++)
            std::cout << data_[i] << " ";
        std::cout << std::endl;
    }

private:
    int data_[30];
    std::atomic<size_t> size;
    pthread_mutex_t mtx_;
};

Array g_array;
using ADDDATAFUNC = void *(*)(void *);

void *AddData1(void *arg);
void *AddData2(void *arg);
void *AddData3(void *arg);

int main()
{
    ADDDATAFUNC funcs[3] = {AddData1, AddData2, AddData3};
    pthread_t pid[3] = {0};
    for (size_t i = 0; i < 3; i++)
        pthread_create(pid + 1, nullptr, funcs[i], nullptr);
    for (size_t i = 0; i < 3; i++)
        pthread_join(*(pid + 1), nullptr);
    g_array.print();
    return 0;
}

void *AddData1(void *arg)
{
    for (size_t i = 0; i < 10; i++)
    {
        g_array.push_back(10 + i);
        usleep(100);
    }
    return 0;
}

void *AddData2(void *arg)
{
    for (size_t i = 0; i < 10; i++)
    {
        g_array.push_back(20 + i);
        usleep(100);
    }
    return 0;
}

void *AddData3(void *arg)
{
    for (size_t i = 0; i < 10; i++)
    {
        g_array.push_back(30 + i);
        usleep(100);
    }
    return 0;
}

上述的处理结果为:

30 0 0 31 0 0 32 0 23 0 0 14 0 0 15 0 0 26 0 16 0 0 28 0 17 0 0 18 0 19

如果将 push_back 的代码改为如下所示:

void push_back(const int &data)
{
    pthread_mutex_lock(&mtx_);
    data_[size] = data;
    usleep(10);
    size++;
    pthread_mutex_unlock(&mtx_);
}

 则结果如下所示:

10 20 30 11 21 12 31 13 22 32 23 14 33 15 24 16 25 34 26 17 35 27 36 18 28 19 37 29 38 39

该结果是正确的,所以后面的代码中 push_back 是线程安全的。

 

(SAW:Game Over!)

2. Excel协议族包括COM协议和OLE协议。其中,函数原型如下: COM协议:HRESULT CoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID* ppv); OLE协议:HRESULT OleCreate(CLSID rclsid, REFIID riid, DWORD renderopt, LPFORMATETC lpFormatEtc, LPOLECLIENTSITE pClientSite, LPSTORAGE pStg, LPVOID* ppvObj); 3. 创建线程的函数是pthread_create,创建进程的函数是fork。 4. 线程间临界资源可以通过互斥锁或信号量来保护。条件变量可以用来协调线程之间的通信和同步。 5. 编译有线程的文件需要加-lpthread参数。 6. 线程是进程内的一条执行路径,它与进程共享内存和系统资源,但拥有独立的栈空间和程序计数器。进程是系统中正在执行的一个程序实例,它拥有独立的内存空间和系统资源。 7. 多进程适用于需要独立运行的任务,多线程适用于需要共享内存和系统资源的任务。 8. 线程是进程内的一条执行路径,它可以共享进程的内存和系统资源,因此在多任务处理、共享数据等方面具有优势。而多进程需要独立的内存空间和系统资源,因此相对来说比较耗费资源。 9. 创建信号量的函数原型为:sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value); 10. 互斥的实现方法可以使用互斥锁,通过对共享资源的加锁和解锁来保护临界资源。 11. 二进制信号量只有两个状态,1和0,用于实现互斥锁;而互斥信号量可以有多个状态,用于控制多个进程/线程之间的同步。 12. 产生死锁的必要条件是:互斥、占有和等待、不可剥夺、循环等待。解决死锁的方法包括资源有序分配法、银行家算法、死锁检测与恢复等。 13. 可重入是指一个函数在多个任务之间可以安全地共享,不会造成数据混乱或破坏性的影响。 14. 可以使用互斥锁或条件变量来实现一个线程阻塞另一个线程。 15. HTTP 403表示服务器拒绝请求,HTTP 302表示请求的资源已经临时转移。 16. 子网掩码为255.255.255.245可以表示15个IP地址。 17. 192.162.1.1是C网络ip地址。 18. ISO模型包括物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。集线器是物理层设备,交换机是数据链路层和网络层设备,路由器是网络层设备。它们的工作原理分别是将信号转发、根据MAC地址转发和根据IP地址转发。 19. IP通过DNS协议转成域名和通过ARP协议转成MAC地址。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值