线程同步

Posted Lune-Qiu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程同步相关的知识,希望对你有一定的参考价值。

今天要写的小程序是卖票

结果如下:共100张票,10个线程

需要声明的一些变量:

public:
    volatile int m_tickets;//原子访问
     static unsigned __stdcall  ThreadProc(void* lparam);
     CListBox m_lstbox;
     map<unsigned int , int> IDMap;//将线程ID和0~10对应
     bool m_flag;
     CRITICAL_SECTION  m_cs;//临界区
     HANDLE m_Mutex;//互斥量

按钮的处理函数:

void CSoldTicketDlg::OnBnClickedButton1()
{
    unsigned int IDnum;
    for(int i = 0; i < 10; i++)
    {
        _beginthreadex(NULL,0,&ThreadProc,this,0,&IDnum);
        IDMap[IDnum] = i;
    }
}

 

线程同步有两种:

 

一 . 原子访问:指的是一个线程在访问某个资源的同时,能够保证没有其他线程会在同一时刻访问同一资源。Interlocked系列。

volatile,:防止编译优化(从寄存器中取值,相对于从外存取值更节省时间,但造成了不能取到的值不能及时更新),对特殊地址的稳定访问。

缺点:只能实现对一个32位或者64位值的单独访问,不能实现复杂的功能。

二 . 关键段(临界区):同一时刻只一个线程访问代码段

1.普通锁

IniticalizeCriticalsection(&g_cs); //初始化CRITICAL_SECTION 中的成员变量

EnterCriticalSection(&g_cs); //线程用它来检查占用标志的函数

LeaveCriticalSection(&g_cs); //离开“卫生间”,门上重新置为无人

DeleteCriticalsection(&g_cs); //删除事件的内核对象,以及为CRITICAL_SECTION初始化的资源。

初始化函数:InitializeCriticalSection(&m_cs);

在Destroy函数中:

void CSoldTicketDlg::OnDestroy()
{
    
    CDialogEx::OnDestroy();
    DeleteCriticalSection(&m_cs);
    
}

线程执行的函数:

 unsigned __stdcall  CSoldTicketDlg::ThreadProc(void* lparam)
 {
    CSoldTicketDlg* pthis = (CSoldTicketDlg*)lparam;
    int num = pthis->IDMap[GetCurrentThreadId()];
    while(1)
    {
        if(pthis->m_tickets <= 0 )
            break;
        Sleep(100);
        CString str;
         EnterCriticalSection(&pthis->m_cs);//锁住
        if(pthis->m_tickets > 0)//解决负数问题
        {
            str.Format(_T("第%d窗口,卖出第%d张票"),num , pthis->m_tickets--);
        } 
        LeaveCriticalSection(&pthis->m_cs);
        pthis->m_lstbox.AddString(str);
    }
    return 0;

 }

 

2.旋转锁:

InitializeCriticalSectionAndSpinCount:旋转锁不断循环,尝试在一段时间内获得访问权。只有当尝试失败的时候,线程才会切换到内核模式并进入等待状态。

SetCriticalSectionSpinCount(   //改变旋转锁的次数

在初始化函数中:InitializeCriticalSectionAndSpinCount(&m_cs,1);

3.异步

TryEnterCriticalSection:线程在访问时,如果不能访问资源,那么它继续做其他事情,而不用等待。

将EnterCriticalSection换为

if(!TryEnterCriticalSection(&pthis->m_cs))//异步
continue;

三 . 互斥量

初始化函数:m_Mutex = CreateMutex(0,FALSE,0);//第二个参数为创建互斥量的进程没有优先级

 unsigned __stdcall  CSoldTicketDlg::ThreadProc(void* lparam)
 {
    CSoldTicketDlg* pthis = (CSoldTicketDlg*)lparam;
    int num = pthis->IDMap[GetCurrentThreadId()];
    while(1)
    {
        if(pthis->m_tickets <= 0 )
            break;
        Sleep(100);
        CString str;
        if(WAIT_TIMEOUT == WaitForSingleObject(pthis->m_Mutex,50))
             continue;
        if(pthis->m_tickets > 0)//解决负数问题
        {
            str.Format(_T("第%d窗口,卖出第%d张票"),num , pthis->m_tickets--);
        } 
        ReleaseMutex(pthis->m_Mutex);
        pthis->m_lstbox.AddString(str);
    }
    return 0;

 }

 协同:

四  .  事件

五 . 信号量

 

函数声明:

    CProgressCtrl m_a;
    CProgressCtrl m_b;
    CProgressCtrl m_c;
    CProgressCtrl* m_arr[3];
    static DWORD WINAPI ThreadProcA( LPVOID lpParameter);
    afx_msg void OnBnClickedButton1();
    afx_msg void OnBnClickedButton2();
    HANDLE m_semaphore;

 

初始化:BOOL CRunDlg::OnInitDialog()

    m_a.SetStep(1);
    m_b.SetStep(5);
    m_arr[0] = &m_a;
    m_arr[1] = &m_b;
    m_arr[2] = &m_c;

ready按钮:

void CRunDlg::OnBnClickedButton1()
{
    for(int i = 0; i < 3; i++)
    {
        CreateThread(0,0,&ThreadProcA,(LPVOID)i,0,0);
    }
}

go按钮:

void CRunDlg::OnBnClickedButton2()
{
    ReleaseSemaphore(m_semaphore,3,0);
}

线程执行函数:

 DWORD WINAPI CRunDlg::ThreadProcA( LPVOID lpParameter)
 {
     CRunDlg* pthis = (CRunDlg*)theApp.m_pMainWnd;
    int i = (int)lpParameter;
    WaitForSingleObject(pthis->m_semaphore ,INFINITE);
    while(1)
    {
        (pthis->m_arr[i])->StepIt();
        Sleep(100);
    }
 }

 

以上是关于线程同步的主要内容,如果未能解决你的问题,请参考以下文章

起底多线程同步锁(iOS)

多线程编程

第十次总结 线程的异步和同步

详解C++多线程

进程线程同步异步

配置 kafka 同步刷盘