首页
社区
课程
招聘
[旧帖] [原创]说说多线程(进程)项目开发的点点体会 0.00雪花
发表于: 2010-4-1 14:44 2876

[旧帖] [原创]说说多线程(进程)项目开发的点点体会 0.00雪花

2010-4-1 14:44
2876

多线程开发是软件开发的一个难点,下面说一说我多年的开发体会:

数据保护:

如果一个资源(一般是一组相关数据)如果可能“同时”被多个线程访问,为了保证数据的“一致性”,我们应该对该资源进行保护。

一般是使用一个同步对象来保证没有多个线程同时对该资源“同时访问”,以Windows为例,常用的同步对象有CriticalSection、Mutex等待,CriticalSection只能够用于同一个进程中的线程间同步,而Mutex可以用于不同进程之间,我们一般吧获得一个同步对象称为对一个资源加锁,反之称为解锁。

一个线程要想读写需要保护的资源,先要对该资源加锁,读写完毕后必须解锁。

一般对资源的操作时原子的,那就不会破坏数据的完整性,一般不用保护,如多个线程可以对一个DWORD的值进行读写,不会有问题。

为了提高程序的性能,如果多个线程仅仅同时读一个资源,可以不加保护,但是只要有一个线程在修改保护资源,其他线程对该保护资源也就不能读也不能写了。

关于死锁:

多线程编程的一个难点不是仅仅对资源保护这么简单,一个头痛的问题是如果规划不好,会导致多个线程死锁,比如:线程1在占有一个同步对象A时,企图获取线程2已经占有的一个同步对象B,而线程2而此刻也在企图获取同步对象A,这样线程1和线程2就进入了一种称作“死锁”的状态,谁也不能往下走了。

显然产生死锁是有条件的,1锁嵌套,2是不同线程加锁顺序不同。保证所有线程按照同一个顺序获取同步对象似乎就能解决死锁的问题了,然而在实际开发中,做到这一点是很难的,而且死锁并不是仅仅相互等待被对方占有的一把锁这么简单。

死锁有时候会很“隐蔽”,考虑这样一种情况:工作线程在没有释放锁A时,调用“SetWindowText”修改主线程的一个窗口的标题,而主线程此刻正试图获取被工作线程占有的A锁,会怎么样呢?显然主线程在没有得到A锁时,是动不了的,而工作线程呢?工作线程给主线程Send了一个消息,在主线程没有反馈前,是不会往下走的,而此刻主线程也不会处理工作线程Send过来的消息,这样他们就死锁了。

解决消息死锁的方法是:主线程使用MsgWaitForMultiObjects来获取锁,该API可以在等待的锁被释放或者有其他线程Send消息时返回,就可以做到边等边看,在等待锁的同时处理消息。

死锁的情况远不止上面说的,其实不但消息和同步对象之间可以锁死,而且消息之间也可能发生死锁,还可能多个线程循环等死。仔细研究一下InSendMessage和ReplyMessage的使用场合你会发现还有你没想到的死锁方法,呵呵!

死锁的检测手段:
如果你已经发布的程序在客户那里处了问题,如何确定是否是死锁了?是哪几个线程出了问题?在哪里发生了死锁呢?
。。。
如果有好心人给一个邀请码,就继续往下说,如果没有就算了,呵呵!


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 7
支持
分享
最新回复 (9)
雪    币: 184
活跃值: (41)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
杯具了,居然没有人搭理我,难道这个话题是太小儿科了?

其实我还是认为多线程开发是非常难的,当代码量到了一定程度,复杂度也大大增加,出了问题也很难定位,缺陷再现性也差。

我做了多年的软件维护工作,解决问题无数,绝大多数和多线程相关,呵呵!
2010-4-1 21:07
0
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
期待下文

多线程同步问题理论的东东看的太多了,希望楼主写一些实际中应用的东东
2010-4-2 00:26
0
雪    币: 4
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
要是个多线程的密码保护问题就好了
2010-4-2 06:46
0
雪    币: 31
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
楼主一遍慢慢说,一遍等邀请码啊,只要讲的好,相信斑竹一定会给邀请码的
2010-4-2 09:22
0
雪    币: 184
活跃值: (41)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
下面放一段加锁解锁代码,欢迎指出不足,呵呵:

CGuard::CGuard(HANDLE hMutex,BOOL bWaitMsg,DWORD dwTimeOut)
{
        m_hMutex = hMutex;
        BOOL bLoged = FALSE;

        if (NULL != m_hMutex)
        {
                if (bWaitMsg)
                {
                        for (;;)
                        {
                                DWORD dwResult = ::MsgWaitForMultipleObjects(1, &m_hMutex, FALSE, dwTimeOunt, QS_SENDMESSAGE);
                                if (WAIT_TIMEOUT == dwResult)
                                {
                                        if (!bLoged)
                                        {
                                                PrintStackTrace();
                                                bLoged = TRUE;
                                        }
                                        continue;
                                }

                                if (WAIT_OBJECT_0 == dwResult)
                                {
                                        break;
                                }
                                else
                                {
                                        MSG msg;
                                        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
                                        {
                                                TranslateMessage(&msg);
                                                DispatchMessage(&msg);
                                               
                                                dwResult = ::MsgWaitForMultipleObjects(1, &m_hMutex, FALSE, 0, QS_SENDMESSAGE);
                                       
                                                if (WAIT_OBJECT_0 == dwResult)
                                                {
                                                        return;
                                                }
                                        }
                                }
                       
                        }
                }
                else
                {
                        for (;;)
                        {
                                DWORD dwResult = WaitForSingleObject(m_hMutex,dwTimeOunt);
                                if (WAIT_TIMEOUT == dwResult)
                                {
                                        if (!bLoged)
                                        {
                                                PrintStackTrace();
                                                bLoged = TRUE;
                                        }
                                }
                                else if (WAIT_OBJECT_0 == dwResult)
                                {
                                        return;
                                }
                        }
                }
        }
}

CGuard::~CGuard()
{
        if (NULL != m_hMutex)
        {
                ReleaseMutex(m_hMutex);
        }
}

void CGuard::PrintStackTrace()
{

        DWORD dwAddr         = 0;
        DWORD dwChildEbp = 0;

        _asm call $+5;
        _asm pop eax;
        _asm mov dwAddr,eax;
        _asm mov dwChildEbp,ebp;

        DWORD dwRetAddr = *(DWORD*)(dwChildEbp+4);

        CString strMsg =        "Addr\t\tChildEbp\t\tRetAddr\r\n"
                                                "===============================\r\n";
        try
        {
                while (TRUE)//dwRetAddr != 0)
                {
                        dwAddr = dwRetAddr;
                        dwChildEbp = *(DWORD*)dwChildEbp;
                        dwRetAddr = *(DWORD*)(dwChildEbp+4);

                        CString strTemp;
                        strTemp.Format("0x%08X\t0x%08X\t0x%08X\r\n",dwAddr,dwChildEbp,dwRetAddr);
                        strMsg += strTemp;
                }
        }
        catch (...)
        {
               
                WriteToLogFile(strMsg);
        }
}
2010-4-2 10:08
0
雪    币: 184
活跃值: (41)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7
算了,有始有终把,一般死锁检测都是分析各个线程的函数调用栈
细心的朋友可能从上面的加锁代码中已经发现了方法,就是等待锁超时时将函数调用栈Log下来分析

斑竹好小气,我写了这么多字都要不来一个邀请码,还好我的一个朋友送了一个,呵呵!
2010-4-3 09:25
0
雪    币: 370
活跃值: (52)
能力值: ( LV13,RANK:350 )
在线值:
发帖
回帖
粉丝
8
你写的文章都很不错的 学习了
最近有些忙 所以现在我一般是10天发一次 这次因为有事耽搁了两三天
给各位带来的不便请见谅
该有总是会有的~
2010-4-6 19:50
0
雪    币: 184
活跃值: (41)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
9
版主,客气了,呵呵!
2010-4-7 09:31
0
雪    币: 29
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
多线程的确是很麻烦
2010-4-7 10:09
0
游客
登录 | 注册 方可回帖
返回
//