C++中这种内存怎么优雅释放?

ccnyou 2014-04-25 12:52:58
常见的写法,假设我要开一个线程,线程入口函数声明如下:

void CALLBACK WorkCallBack(PTP_CALLBACK_INSTANCE instance, PVOID context, PTP_WORK work)

Windows下面这种写法是常见的。参数定义成 void* 是为了方便传任意数据进去。
当我需要传一个对象进去,而这个对象是临时产生的,一般来说是

void myfun()
{
const my_block* pblock = new my_block(block);

auto workItem = CreateThreadpoolWork(WorkCallBack, (PVOID) pblock, NULL);

SubmitThreadpoolWork(workItem);

CloseThreadpoolWork(workItem);
}


这时候 pblock 已经不需要了,这样子的话,貌似只能在 WorkCallBack 里面 delete 。
如果在里面delete,就违反了C++的谁分配谁释放原则,而且这个可能记得,写多几个就容易出现内存泄漏了。
而且由于声明是 void* ,C++里面的各种智能指针没法用。想问下你们都是怎么处理的,优雅?
...全文
762 36 打赏 收藏 转发到动态 举报
写回复
用AI写文章
36 条回复
切换为时间正序
请发表友善的回复…
发表回复
赵4老师 2014-04-28
  • 打赏
  • 举报
回复
int PutToRBuf(int cn, CRITICAL_SECTION *cs, FIFO_BUFFER *fbuf, char *buf, int len) {
    int lent;

    Lock(cs);
    lent = len;//先假定本次能放进缓冲区的字节数lent=想要放进缓冲区的字节数len
    if (fbuf->size + lent > BSIZE) {//缓冲区中数据当前字节数+要放进缓冲区的字节数>缓冲区最多能容纳字节数
        lent = BSIZE - fbuf->size;//重新计算本次能放进缓冲区的字节数lent
    }

    if (fbuf->tail + lent > BSIZE) {//缓冲区中数据尾部对应字节偏移量+本次能放进缓冲区的字节数>缓冲区最多能容纳字节数
        //将本次能放进缓冲区的字节分两段,
        int len1 = BSIZE - fbuf->tail;//第一段长度len1
        memcpy(fbuf->data + fbuf->tail, buf, len1);//第一段放在缓冲区中数据尾部之后
        int len2 = lent - len1;//第二段长度len2
        memcpy(fbuf->data, buf + len1, len2);//第二段放在缓冲区开始
        fbuf->tail = len2;//新数据尾部偏移量=len2
    } else {//缓冲区中数据尾部对应字节偏移量+要放进缓冲区的字节数<=缓冲区最多能容纳字节数
        memcpy(fbuf->data + fbuf->tail, buf, lent);//将本次能放进缓冲区的字节不用分两段,直接放在缓冲区中数据尾部之后
        fbuf->tail += lent;//新数据尾部偏移量=旧数据尾部偏移量+lent
    }

    fbuf->size += lent;//新缓冲区中数据当前字节数=旧缓冲区中数据当前字节数+lent
    Unlock(cs);
    return lent;//返回本次实际放进缓冲区的字节数(0..len)
}

ccnyou 2014-04-28
  • 打赏
  • 举报
回复
引用 24 楼 zhao4zhong1 的回复:
代码功能归根结底不是别人帮自己看或讲解或注释出来的;而是被自己静下心来花足够长的时间和精力亲自动手单步或设断点或对执行到某步获得的中间结果显示或写到日志文件中一步一步分析出来的。
提醒:再牛×的老师也无法代替学生自己领悟和上厕所!
单步调试和设断点调试是程序员必须掌握的技能之一。


所谓的断点调试:


代码我稍微修改了下:



MYVOID thdB(void *pcn) {
char *recv_buf;
int recv_nbytes;
int cn;
int wc;
int pb;

cn = (int) pcn;
Log("%03d thdB thread begin...\n", cn);
while (1) {
sleep_ms(10);
recv_buf = (char *) g_bufferC;
recv_nbytes = CSIZE;
wc = 0;
while (1) {
Sleep(1000); //假设读的人没那么及时
pb = GetFromRBuf(cn, &cs_BBB, &g_fifo_buffer, recv_buf, recv_nbytes);
if (pb) {
Log("%03d recv %d bytes\n", cn, pb);
HexDump(cn, recv_buf, pb);
sleep_ms(1);
} else {
sleep_ms(1000);
}
if (No_Loop) break;//
wc++;
if (wc > 3600) Log("%03d %d==wc>3600!\n", cn, wc);
}
if (No_Loop) break;//
}
#ifndef WIN32
pthread_exit(NULL);
#endif
}

//这里没修改,只是注释和命名整理

int PutToRBuf(int cn, CRITICAL_SECTION *cs, FIFO_BUFFER *fbuf, char *buf, int len) {
int lent;

Lock(cs);
lent = len;
if (fbuf->size + lent > BSIZE) {
//肯定装不下,截断,只装前面部分
lent = BSIZE - fbuf->size;
}

if (fbuf->tail + lent > BSIZE) {
//这时候缓冲区满了,装不下,再截断
int len1 = BSIZE - fbuf->tail;
memcpy(fbuf->data + fbuf->tail, buf, len1);
//然后把剩下的写到开头
int len2 = lent - len1;
memcpy(fbuf->data, buf + len1, len2);
fbuf->tail = len2;
} else {
memcpy(fbuf->data + fbuf->tail, buf, lent);
fbuf->tail += lent;
}

// 这里没法理解了, lent = BSIZE - fbuf->size的话,
// 那么 fbuf->size + lent = BSIZE
// 那么,下次 lent = BSIZE - fbuf->size;
// lent = 0 了
// 如果 对方读得比较满的话,就再也没法写东西进去了
fbuf->size += lent;
Unlock(cs);
return lent;
}
ccnyou 2014-04-28
  • 打赏
  • 举报
回复
引用 35 楼 qingcairousi 的回复:
既然都上auto了,为什么还不用std::thread?
看代码- - 这个是微软,线程池API
qingcairousi 2014-04-28
  • 打赏
  • 举报
回复
既然都上auto了,为什么还不用std::thread?
神-气 2014-04-28
  • 打赏
  • 举报
回复
你需要C++11,各种优雅 !
赵4老师 2014-04-28
  • 打赏
  • 举报
回复
任何问题首先是一个哲学问题! 这个世界到底是有限的还是无限的? 目前具体的事物除时间和空间可能是无限的以外,其它任何事物都是有限的。
事后猪葛 2014-04-28
  • 打赏
  • 举报
回复
引用 21 楼 zhao4zhong1 的回复:
[quote=引用 20 楼 qinchange 的回复:] [quote=引用 10 楼 zhao4zhong1 的回复:] 在主循环中动态创建销毁线程+动态创建销毁对象=作死! 在初始化时创建线程池+创建对象池,在主循环中杜绝一切动态创建释放=安逸!
大神,我一直想问你,这个头像是你本人吗??[/quote] 当然是了.[/quote]我看了这么久,这画质能清晰点吗?这么的AV画质,看久了对视力不好
赵4老师 2014-04-28
  • 打赏
  • 举报
回复
你对一个脑子反应速度比你慢比如10倍的人说话,你能做到你说过的每个字都被对方理解且一直保持你快速的喋喋不休?可能吗?!
赵4老师 2014-04-28
  • 打赏
  • 举报
回复
任何号称万能的队列或消息系统都早晚会出现写不了东西进去的情况,且一旦出现这种情况,如果没有人去干预,是无法解决的。
赵4老师 2014-04-28
  • 打赏
  • 举报
回复
不解释,试看运行结果。 if (wc > 3600) Log("%03d %d==wc>3600!\n", cn, wc);//真出现这条log的话,需要人工干预才能解决问题。要知道不是啥问题都能指望靠预先写好的代码自动纠正的!否则所谓能或之前能自动纠错的代码会犯下无可挽回的新的更大的错误!!
ccnyou 2014-04-28
  • 打赏
  • 举报
回复
引用 26 楼 zhao4zhong1 的回复:
int PutToRBuf(int cn, CRITICAL_SECTION *cs, FIFO_BUFFER *fbuf, char *buf, int len) {
    int lent;

    Lock(cs);
    lent = len;//先假定本次能放进缓冲区的字节数lent=想要放进缓冲区的字节数len
    if (fbuf->size + lent > BSIZE) {//缓冲区中数据当前字节数+要放进缓冲区的字节数>缓冲区最多能容纳字节数
        lent = BSIZE - fbuf->size;//重新计算本次能放进缓冲区的字节数lent
    }

    if (fbuf->tail + lent > BSIZE) {//缓冲区中数据尾部对应字节偏移量+本次能放进缓冲区的字节数>缓冲区最多能容纳字节数
        //将本次能放进缓冲区的字节分两段,
        int len1 = BSIZE - fbuf->tail;//第一段长度len1
        memcpy(fbuf->data + fbuf->tail, buf, len1);//第一段放在缓冲区中数据尾部之后
        int len2 = lent - len1;//第二段长度len2
        memcpy(fbuf->data, buf + len1, len2);//第二段放在缓冲区开始
        fbuf->tail = len2;//新数据尾部偏移量=len2
    } else {//缓冲区中数据尾部对应字节偏移量+要放进缓冲区的字节数<=缓冲区最多能容纳字节数
        memcpy(fbuf->data + fbuf->tail, buf, lent);//将本次能放进缓冲区的字节不用分两段,直接放在缓冲区中数据尾部之后
        fbuf->tail += lent;//新数据尾部偏移量=旧数据尾部偏移量+lent
    }

    fbuf->size += lent;//新缓冲区中数据当前字节数=旧缓冲区中数据当前字节数+lent
    Unlock(cs);
    return lent;//返回本次实际放进缓冲区的字节数(0..len)
}

行行注释也没法说明问题- - 如果你这边写的线程比读的线程频繁,肯定会遇到写不进去的。 这里说明了: // 这里没法理解了, lent = BSIZE - fbuf->size的话, // 那么 fbuf->size + lent = BSIZE // 那么,下次 lent = BSIZE - fbuf->size; // lent = 0 了 // 如果 对方读得比较满的话,就再也没法写东西进去了 并且也断点证明了,的确会出现写不了东西进去。 或者,大大能从原理性解释下你这样做法的可行性? 用现用的缓冲区总要解决几个问题: 1,不够大 2,写满了怎么处理 3,谁清理 或许可以用动态数据结构处理,但是谁清理又回归原始问题,总不能一个动态数组不断膨胀,那跟内存泄漏没区别了。 读者读完处理那还不跟传一个new过去给他delete
「已注销」 2014-04-28
  • 打赏
  • 举报
回复
优雅是个唯心的词。。。。。。。你认为优雅的那就是优雅的你认为不是就不是。有人认为智能指针是优雅的有的人却不这么认为。 只要保证能正确释放即可。至于优雅,让他的飘在梦中吧。
赵4老师 2014-04-27
  • 打赏
  • 举报
回复
引用 22 楼 ccnyou 的回复:
[quote=引用 21 楼 zhao4zhong1 的回复:] [quote=引用 20 楼 qinchange 的回复:] [quote=引用 10 楼 zhao4zhong1 的回复:] 在主循环中动态创建销毁线程+动态创建销毁对象=作死! 在初始化时创建线程池+创建对象池,在主循环中杜绝一切动态创建释放=安逸!
大神,我一直想问你,这个头像是你本人吗??[/quote] 当然是了.[/quote] 大大上面给出例子是,a拷贝到全局buffer里面,然后b从里面读。但是问题来了,如果a拷贝了两次过去,b才过来读一次,那么a第一次写过去的东西不是被覆盖了?[/quote] 我的程序不会覆盖,你错误地认为它覆盖了. 你还是没弄明白我的代码. 代码功能归根结底不是别人帮自己看或讲解或注释出来的;而是被自己静下心来花足够长的时间和精力亲自动手单步或设断点或对执行到某步获得的中间结果显示或写到日志文件中一步一步分析出来的。 提醒:再牛×的老师也无法代替学生自己领悟和上厕所! 单步调试和设断点调试是程序员必须掌握的技能之一。
ccnyou 2014-04-25
  • 打赏
  • 举报
回复
引用 10 楼 zhao4zhong1 的回复:
在主循环中动态创建销毁线程+动态创建销毁对象=作死! 在初始化时创建线程池+创建对象池,在主循环中杜绝一切动态创建释放=安逸!
更有复杂的,有时候要 PostMessage 中附带一个结构体,怎么处理好呢?即使像1楼那种方案也会有泄漏几率。
ccnyou 2014-04-25
  • 打赏
  • 举报
回复
引用 10 楼 zhao4zhong1 的回复:
在主循环中动态创建销毁线程+动态创建销毁对象=作死! 在初始化时创建线程池+创建对象池,在主循环中杜绝一切动态创建释放=安逸!
大大能不能举一点具体的例子呢?或者简单的代码段?理解能力拙计啊。 像我上面举的例子,怎么样做算是作死的,怎么做才算安逸的?
赵4老师 2014-04-25
  • 打赏
  • 举报
回复
在主循环中动态创建销毁线程+动态创建销毁对象=作死! 在初始化时创建线程池+创建对象池,在主循环中杜绝一切动态创建释放=安逸!
buyong 2014-04-25
  • 打赏
  • 举报
回复
设计如果不能该,就在线程里delete,不过我觉得还是应该想办法,比如分配在栈上?或者在线程里面new,通过消息传一些信息?
mujiok2003 2014-04-25
  • 打赏
  • 举报
回复
引用 17 楼 super_admi 的回复:
WINDOWS的API, 一般都是由调用方提供已分配的内存吧?然后由调用方释放,这是基本原则。
不是的。 比如一个消息系统, 一个生产,一个消费,这也常见。
ccnyou 2014-04-25
  • 打赏
  • 举报
回复
引用 21 楼 zhao4zhong1 的回复:
[quote=引用 20 楼 qinchange 的回复:] [quote=引用 10 楼 zhao4zhong1 的回复:] 在主循环中动态创建销毁线程+动态创建销毁对象=作死! 在初始化时创建线程池+创建对象池,在主循环中杜绝一切动态创建释放=安逸!
大神,我一直想问你,这个头像是你本人吗??[/quote] 当然是了.[/quote] 大大上面给出例子是,a拷贝到全局buffer里面,然后b从里面读。但是问题来了,如果a拷贝了两次过去,b才过来读一次,那么a第一次写过去的东西不是被覆盖了?
mujiok2003 2014-04-25
  • 打赏
  • 举报
回复
引用 6 楼 ccnyou 的回复:
好吧我错了,auto_ptr::release 只会简单释放所有权,并不会 delete 对象。
yeah
加载更多回复(16)

64,651

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧