线程同步(一)—— 互斥锁

在使用线程时,经常要注意的就是访问临界资源加锁。 在编码过程由于粗心忘记加锁将带来不可预知的错误。这类错误单次运行或小并发时难以复现,当数据量变大,用户数增多时,轻则系统崩溃,大则引起数据错误。造成损失。 线程中互斥锁与进程的信号量类似,也可以看做是PV操作,用于保护临界资源,确保只有一个线程访问。

下面代码是不加锁错误代码,其中也涉及到之前提到的线程编程时需要注意的一些小细节。

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

using namespace std;

class ThreadInterface
{
public:
    void CreateThread(void* (*func)(void *));
    void WaitThread();
private:
    pthread_t m_pTread;   
};

void ThreadInterface::CreateThread(void* (*func)(void *))
{
    pthread_create(&m_pTread, NULL, func, NULL); 
}

void ThreadInterface::WaitThread()
{
    pthread_join(m_pTread, NULL); 
}

class MutexLockInterface
{
public:
    void CreateMutexLock();
    void GetMutexLock();
    void ReleaseMutexLock();
    void DestroyMutexLock();
private:  
    pthread_mutex_t m_MutexLock;  
};

void MutexLockInterface::CreateMutexLock()
{
    int ret = pthread_mutex_init(&m_MutexLock, NULL);
    if (0 != ret)
        cout<<"init mutex error!";
}

void MutexLockInterface::GetMutexLock()
{
    pthread_mutex_lock(&m_MutexLock);
}

void MutexLockInterface::ReleaseMutexLock()
{
    pthread_mutex_unlock(&m_MutexLock);
}

void MutexLockInterface::DestroyMutexLock()
{
    pthread_mutex_destroy(&m_MutexLock);
}


class Service
{
public:
    static void* run(void *)    //类成员线程函数为static去除this指针
    {
        //m_MutexLock.GetMutexLock();
        if (0 == m_Tickets)
        {
            cout<<"stop operate!"<<endl;
        }
        else
        {
            cout<<"window2:we have "<<m_Tickets<<"Tickets"<<endl;
            sleep(1);
            --m_Tickets;
        }
        //m_MutexLock.ReleaseMutexLock();
    }
    int SetData(int data){m_Tickets = data;};
    int GetData(){return m_Tickets;};
    static int m_Tickets;
    static MutexLockInterface m_MutexLock;
};
int Service::m_Tickets = 1; //静态变量类外初始化
MutexLockInterface Service::m_MutexLock;

int main()
{
    Service Srv;
    ThreadInterface Thread;
    Srv.m_MutexLock.CreateMutexLock();
    
    Thread.CreateThread(&Srv.run);

    //Srv.m_MutexLock.GetMutexLock();
    if (0 == Srv.GetData())
    {
        cout<<"stop operate!"<<endl;
    }
    else
    {
        cout<<"window1:we have "<<Srv.GetData()<<"Tickets"<<endl;
        sleep(1);  //延时1s等待线程2
        Srv.SetData(Srv.GetData() - 1);
    }
    //Srv.m_MutexLock.ReleaseMutexLock();
    Thread.WaitThread();    //等待线程结束回收
    cout<<Srv.GetData()<<endl;
    return 0;
}

上述代码以售票为场景,当票只剩下一张时,两个窗口同时有人需要购票。

线程不加锁,执行结果如下:

很显然这不是我们想要的结果,只有一张票却卖出去了两张,最后余票显示为-1!

去除注释行,对临界资源操作是加锁,再运行程序,得到与预期一致的结果!

这就是线程互斥锁存在的原因。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏用户2442861的专栏

web.xml配置详解

1、web.xml学名叫部署描述符文件,是在Servlet规范中定义的,是web应用的配置文件。

3921
来自专栏FreeBuf

恶意程序分析利器PowerShellArsenal

简介 PowerShellArsenal是一个PowerShell模块,它的功能是帮助逆向工程师来分析.NET恶意软件,PowerShellArsenal的功能...

2629
来自专栏软件开发

前端MVC Vue2学习总结(七)——ES6与Module模块化、Vue-cli脚手架搭建、开发、发布项目与综合示例

使用vue-cli可以规范项目,提高开发效率,但是使用vue-cli时需要一些ECMAScript6的知识,特别是ES6中的模块管理内容,本章先介绍ES6中的基...

1686
来自专栏SpringBoot

Swagger2 注解说明

5963
来自专栏分布式系统进阶

Kafka集群Metadata管理Kafka源码分析-汇总

可以看到是调用了ReplicaManager.maybeUpdateMetadataCache方法, 里面又会调用到MetadataCache.updateCa...

2642
来自专栏JavaQ

源码阅读之CyclicBarrier

源码阅读是基于JDK7,本篇主要涉及CyclicBarrier常用方法源码分析。文中代码若格式排版不对,可点击底部的阅读原文阅读。 1.概述 CyclicBar...

3407
来自专栏软件开发

前端MVC Vue2学习总结(七)——ES6与Module模块化、Vue-cli脚手架搭建、开发、发布项目与综合示例

使用vue-cli可以规范项目,提高开发效率,但是使用vue-cli时需要一些ECMAScript6的知识,特别是ES6中的模块管理内容,本章先介绍ES6中的基...

4557
来自专栏腾讯云Elasticsearch Service

Elasitcsearch 底层系列 Lucene 内核解析之 Stored Fields

Lucene 的 stored fields 主要用于行存文档需要保存的字段内容,每个文档的所有 stored fields 保存在一起,在查询请求需要返回字段...

1921
来自专栏Elasticsearch实验室

Elasitcsearch 底层系列 Lucene 内核解析之 Stored Fields

Lucene 的 stored fields 主要用于行存文档需要保存的字段内容,每个文档的所有 stored fields 保存在一起,在查询请求需要返回字段...

6955
来自专栏xingoo, 一个梦想做发明家的程序员

【插件开发】—— 11 窃听风云(Java事件监听原理-GEF实例讲解)

前文回顾: 1 插件学习篇 2 简单的建立插件工程以及模型文件分析 3 利用扩展点,开发透视图 4 SWT编程须知 5 SWT简单控件的使用与布局搭...

2165

扫码关注云+社区

领取腾讯云代金券