首先声明,此帖为错误帖,希望大家能作为反例来看。
转帖目的:这段时间正在设计基于完成端口的服务器,但是稳定性一直不是很好,找了蛮多资料,有帮助的较少,看了此文,收获不少。
当时,就是因为加入的是单线程运行时库,导致new和delete操作出错。后来,在看书的过程中,才知道,windows中,堆是属于进程的,当多个线程对堆进行操作时,是需要加锁的,如果导入的是多线程运行时库,那么,在new和delete时,是加锁来操作的。以后,我将向大家介绍windows的内存管理机制。
这段时间正在做一个模块,总是出错,提示指令引用的内存不能为“read”或“written”。
逐个排查错误,最终,发现是线程处理函数中的new语句那里出了问题。
那么,平时我们如下所用是安全的么?
int *p = new int:
在单线程程序中,这样用是安全的。多线程中呢?
you know,堆内存是多线程共用的。如果两个线程同时对堆内存发出请求,问题就出现了。
你可以进行如下实验:
#include <stdio.h>
#include <windows.h>
DWORD WINAPI ThrdProc(LPVOID lpParam);
int main()
{
int i=0;
HANDLE hThrd[8];
for(i=0; i<8; i++)
{
hThrd[i] = CreateThread(NULL, 0, ThrdProc, 0, NULL, 0);
}
for(i=0; i<8; i++)
{
WaitForSingleObject(hThrd[i], INFINITE);
CloseHandle(hThrd[i]);
}
printf("ok!/n");
return 0;
}
DWORD WINAPI ThrdProc(LPVOID lpParam)
{
int *pi = NULL;
for(int i=0; i<1000; i++)
{
pi = new int;
if(NULL != pi)
{
delete pi;
pi = NULL;
}
}
return 0;
}
此实验中,8个线程频繁申请堆空间
接着,你可以再做一个实验
在上个实验代码中,delete pi;语句前,加一句printf("%d/n",*pi);,然后再运行一下,结果怎样呢?
现在可以总结了:
如果多个线程同时请求堆内存操作,则会引发错误,因为线程共用堆内存。因此,第一个实验中,线程频繁操作堆内存,引起冲突,导致错误出现。
那么,平时我们直接用new等操作,为什么不会出错误呢?你要知道,堆内存的分配及回收,速度是相当快的。在第二个实验中,线程不仅请求堆内存操作,还加了一个printf。与new的执行速度相比,printf慢多了。这样,就大大降低了冲突(线程对堆内存操作)概率。因此,第二个实验运行时极少出错,但不是绝对的安全。我们平时写多线程的代码,也正因为是这样,所以,虽然存在隐患,但是却很少发现它。
解决这个隐患,可以给堆内存加个全局排斥锁,或者临界,只要线程对堆内存的操作(如new、delete,malloc、free),就要申请下
下面是用临界做的测试:
#include <windows.h>
#include <iostream.h>
CRITICAL_SECTION g_lijie;
DWORD WINAPI ThreadFunc(LPVOID lpParam)
{
for(int i=0; i<1000; i++)
{
EnterCriticalSection(&g_lijie);
int *p = new int[10];
LeaveCriticalSection(&g_lijie);
if (p != NULL)
{
i++;
//cout<<p<<endl;
EnterCriticalSection(&g_lijie);
delete[] p;
LeaveCriticalSection(&g_lijie);
}
}
return 1;
}
void main()
{
HANDLE hThread;
InitializeCriticalSection(&g_lijie);
for (int i=0; i<10; i++)
{
hThread = CreateThread(NULL,0,ThreadFunc,NULL,0,NULL);
}
Sleep(100);
}
CRITICAL_SECTION,InitializeCriticalSection,EnterCriticalSection,LeaveCriticalSection,这些是临界区的操作,如果不明白,可以msdn下