cocos2dx多线程以及线程同步 与 cocos2dx内存管理与多线程问题

cocos2dx多线程以及线程同步 与 cocos2dx内存管理与多线程问题,第1张

概述  cocos2dx多线程以及线程同步 与 cocos2dx内存管理与多线程问题 分类: 移动研发 COCOS2DX 移动ANDROID研发 IOS研发 2013-11-07 16:17  7573人阅读  评论(5)  收藏  举报 目录(?)[+] cocos2d-x引擎在内部实现了一个庞大的主循环,每帧之间更新界面,如果耗时的 *** 作放到了主线程中,游戏的界面就会卡,这是不能容忍的,游戏最基本的 cocos2dx多线程以及线程同步 与 cocos2dx内存管理与多线程问题 分类:移动研发COCOS2DX移动ANDROID研发 IOS研发 2013-11-07 16:17 7573人阅读 评论(5) 收藏 举报 @H_419_24@

目录(?)[+]

cocos2d-x引擎在内部实现了一个庞大的主循环,每帧之间更新界面,如果耗时的 *** 作放到了主线程中,游戏的界面就会卡,这是不能容忍的,游戏最基本的条件就是流畅性,这就是为什么游戏开发选择C++的原因。另外现在双核手机和四核手机越来越普遍了,是时候使用多线程来挖掘硬件的潜力了。

1.环境搭建

cocos2d-x中的多线程使用pthread就可以实现跨平台,而且也不是很难理解。使用pthread需要先配置一下工程。右击工程----->属性----->配置属性---->链接器----->输入---->附加依赖项中添加pthreadVCE2.lib,如下图


接着添加附加包含目录,右击项目,属性----->C/C++---->常规----->附加包含目录加入pthread头文件所在的目录


这样,环境就搭建起来了。

2.多线程的使用

使用pthread来实现多线程,最重要的一个函数是

[cpp] view plain copy PTW32_DLLPORTintPTW32_CDECLpthread_create(pthread_t*tID,//线程的标示 constpthread_attr_t*attr,0); background-color:inherit">//创建线程的参数 voID*(*start)(voID*),0); background-color:inherit">//入口函数的指针 voID*arg);//传递给线程的数据 [cpp] view plain copy @H_404_119@ PTW32_DLLPORTintPTW32_CDECLpthread_create(pthread_t*tID,//线程的标示 constpthread_attr_t*attr,0); background-color:inherit">//创建线程的参数 voID*(*start)(voID*),0); background-color:inherit">//入口函数的指针 voID*arg);//传递给线程的数据
在HelloWorldScene.h文件中

copy pthread_tpIDrun,pIDgo; staticvoID*th_run(voID*r); voID*th_go(voID*r); copy pthread_tpIDrun,pIDgo; staticvoID*th_run(voID*r); voID*th_go(voID*r); 定义了两个函数和两个线程的标识。

然后自定义了一个类,用于给线程传递数据。Student类如下:

copy #pragmaonce #include<string> classStudent { public: Student(voID); Student(std::stringname,intage,std::stringsex); ~Student( std::stringname; intage; std::stringsex; }; copy #pragmaonce #include<string> classStudent { public: Student(voID); Student(std::stringname,87); background-color:inherit; Font-weight:bold">intage,std::stringsex); ~Student( std::stringname; intage; std::stringsex; };
源文件如下

copy #include"Student.h" #include"cocos2d.h" Student::Student(voID) { } Student::~Student(voID) cocos2d::cclog("deletedata"); Student::Student(std::stringname,std::stringsex) this
->name=name; this
->age=age; this->sex=sex; } copy #include"Student.h" #include"cocos2d.h" Student::Student(voID) { } Student::~Student(voID) cocos2d::cclog("deletedata"); Student::Student(std::stringname,std::stringsex) this->name=name; this->age=age; this->sex=sex; } 在退出菜单的回调函数中启动两个线程:

copy voIDHelloWorld::menuCloseCallback(CCObject*pSender) Student*temp=newStudent(std::string("zhycheng"),23,std::string("male")); pthread_mutex_init(&mutex,NulL); pthread_create(&pIDrun,NulL,th_run,temp);//启动线程 pthread_create(&pIDgo,th_go,0); } copy voIDHelloWorld::menuCloseCallback(CCObject*pSender) Student*temp=newStudent(std::string("zhycheng"),std::string("male")); pthread_mutex_init(&mutex,NulL); pthread_create(&pIDrun,0); background-color:inherit">//启动线程 pthread_create(&pIDgo,0); }
可以看到,将Student的指针传递给了pIDrun线程,那么在pIDrun线程中获得Student信息如下:

copy Student*s=(Student*)(r); cclog("nameis%s,andageis%d,sexis%s",s->name.c_str(),s->age,s->sex.c_str()); deletes; copy Student*s=(Student*)(r); cclog("nameis%s,sexis%s",s->sex.c_str()); deletes;

3.线程同步 使用了线程,必然就要考虑到线程同步,不同的线程同时访问资源的话,访问的顺序是不可预知的,会造成不可预知的结果。

这里使用pthread_mutex_t来实现同步,下面我来演示一下使用多线程实现卖票系统。卖票的时候,是由多个窗口同时卖票,这里要做到一张票不要卖出去两次,不要出现有票却无法卖的结果。

在线程函数th_run和th_go中来卖票,票的数量是一个全局变量,每卖出去一张票,就将票的数量减一。其中同步的pthread_mutex_t也是一个全局变量,就用它来实现线程同步。

copy voID*HelloWorld::th_run(voID*r) Student*s=(Student*)(r); cclog("nameis%s,s->sex.c_str()); deletes; while(true) pthread_mutex_lock(&mutex); if(ticket>0) cclog("threadrunsell%d",ticket); ticket--; pthread_mutex_unlock(&mutex); } else break; Sleep(1); //Usleep(10); returnNulL; copy voID*HelloWorld::th_run(voID*r) Student*s=(Student*)(r); cclog("nameis%s,s->sex.c_str()); deletes; while(true) pthread_mutex_lock(&mutex); if(ticket>0) cclog("threadrunsell%d",ticket); ticket--; pthread_mutex_unlock(&mutex); } else break; Sleep(1); //Usleep(10); returnNulL; }
copy voID*HelloWorld::th_go(true) pthread_mutex_lock(&mutex); if(ticket>0) cclog("threadgosell%d",ticket); ticket--; pthread_mutex_unlock(&mutex); else break; returnNulL; copy voID*HelloWorld::th_go(true) pthread_mutex_lock(&mutex); if(ticket>0) cclog("threadgosell%d",ticket); ticket--; pthread_mutex_unlock(&mutex); else break; returnNulL; }

mutex被锁定后,其他线程若再想锁定mutex的话,必须等待,当该线程释放了mutex之后,其他线程才能锁定mutex。Sleep()函数可以使得该线程休眠,单位是毫秒。下面是卖票的结果:

copy nameiszhycheng,andageis23,sexismale deletedata threadrunsell100 threadrunsell99 threadgosell98 threadgosell97 threadrunsell96 threadgosell95 threadgosell94 threadrunsell93 threadgosell92 threadrunsell91 threadgosell90 threadgosell89 threadrunsell88 threadgosell87 threadrunsell86 threadgosell85 threadrunsell84 threadgosell83 threadrunsell82 threadgosell81 threadrunsell80 threadgosell79 threadrunsell78 threadgosell77 threadrunsell76 threadgosell75 threadrunsell74 threadgosell73 threadrunsell72 threadgosell71 threadrunsell70 threadgosell69 threadgosell68 threadrunsell67 threadgosell66 threadrunsell65 threadgosell64 threadrunsell63 threadgosell62 threadrunsell61 threadgosell60 threadrunsell59 threadgosell58 threadrunsell57 threadgosell56 threadrunsell55 threadgosell54 threadrunsell53 threadrunsell52 threadgosell51 threadrunsell50 threadgosell49 threadrunsell48 threadgosell47 threadrunsell46 threadgosell45 threadrunsell44 threadrunsell43 threadgosell42 threadrunsell41 threadrunsell40 threadgosell39 threadrunsell38 threadrunsell37 threadrunsell36 threadrunsell35 threadgosell34 threadrunsell33 threadrunsell32 threadgosell31 threadrunsell30 threadrunsell29 threadrunsell28 threadrunsell27 threadrunsell26 threadrunsell25 threadgosell24 threadrunsell23 threadgosell22 threadgosell21 threadrunsell20 threadgosell19 threadrunsell18 threadrunsell17 threadgosell16 threadrunsell15 threadgosell14 threadgosell13 threadrunsell12 threadgosell11 threadgosell10 threadrunsell9 threadgosell8 threadrunsell7 threadgosell6 threadgosell5 threadrunsell4 threadgosell3 threadrunsell2 threadrunsell1 copy nameiszhycheng,sexismale deletedata threadrunsell100 threadrunsell99 threadgosell98 threadgosell97 threadrunsell96 threadgosell95 threadgosell94 threadrunsell93 threadgosell92 threadrunsell91 threadgosell90 threadgosell89 threadrunsell88 threadgosell87 threadrunsell86 threadgosell85 threadrunsell84 threadgosell83 threadrunsell82 threadgosell81 threadrunsell80 threadgosell79 threadrunsell78 threadgosell77 threadrunsell76 threadgosell75 threadrunsell74 threadgosell73 threadrunsell72 threadgosell71 threadrunsell70 threadgosell69 threadgosell68 threadrunsell67 threadgosell66 threadrunsell65 threadgosell64 threadrunsell63 threadgosell62 threadrunsell61 threadgosell60 threadrunsell59 threadgosell58 threadrunsell57 threadgosell56 threadrunsell55 threadgosell54 threadrunsell53 threadrunsell52 threadgosell51 threadrunsell50 threadgosell49 threadrunsell48 threadgosell47 threadrunsell46 threadgosell45 threadrunsell44 threadrunsell43 threadgosell42 threadrunsell41 threadrunsell40 threadgosell39 threadrunsell38 threadrunsell37 threadrunsell36 threadrunsell35 threadgosell34 threadrunsell33 threadrunsell32 threadgosell31 threadrunsell30 threadrunsell29 threadrunsell28 threadrunsell27 threadrunsell26 threadrunsell25 threadgosell24 threadrunsell23 threadgosell22 threadgosell21 threadrunsell20 threadgosell19 threadrunsell18 threadrunsell17 threadgosell16 threadrunsell15 threadgosell14 threadgosell13 threadrunsell12 threadgosell11 threadgosell10 threadrunsell9 threadgosell8 threadrunsell7 threadgosell6 threadgosell5 threadrunsell4 threadgosell3 threadrunsell2 threadrunsell1

可以看到,这个打印结果正确无误。如果不加mutex会是什么样的结果呢,我将线程同步的mutex注释掉,输出的结果为:

copy threadrunsell98 threadrunsell94 threadgosell94 threadrunsell92 threadrunsell89 threadgosell88 threadrunsell87 threadgosell86 threadgosell84 threadrunsell83 threadgosell82 threadrunsell81 threadgosell80 threadrunsell79 threadrunsell75 threadgosell74 threadrunsell73 threadgosell72 threadrunsell71 threadgosell70 threadrunsell68 threadgosell67 threadgosell63 threadrunsell62 threadgosell61 threadrunsell60 threadrunsell58 threadrunsell56 threadrunsell54 threadgosell52 threadrunsell52 threadgosell50 threadrunsell50 threadgosell49 threadrunsell47 threadgosell47 threadrunsell45 threadrunsell43threadgosell43 threadgosell41 threadrunsell39 threadgosell37 threadgosell35 threadrunsell35 threadgosell33threadrunsell33 threadgosell31threadrunsell31 threadgosell29 threadrunsell29 threadgosell27 threadrunsell27 threadgosell25 threadrunsell25 threadgosell23 threadrunsell21 threadgosell21 threadrunsell19 threadgosell17 threadgosell15 threadrunsell15 threadrunsell13 threadgosell13 threadrunsell11threadgosell11 threadgosell9 threadrunsell9 threadgosell7 threadgosell5threadrunsell5 threadrunsell3 threadgosell1 copy threadrunsell98 threadrunsell94 threadgosell94 threadrunsell92 threadrunsell89 threadgosell88 threadrunsell87 threadgosell86 threadgosell84 threadrunsell83 threadgosell82 threadrunsell81 threadgosell80 threadrunsell79 threadrunsell75 threadgosell74 threadrunsell73 threadgosell72 threadrunsell71 threadgosell70 threadrunsell68 threadgosell67 threadgosell63 threadrunsell62 threadgosell61 threadrunsell60 threadrunsell58 threadrunsell56 threadrunsell54 threadgosell52 threadrunsell52 threadgosell50 threadrunsell50 threadgosell49 threadrunsell47 threadgosell47 threadrunsell45 threadrunsell43threadgosell43 threadgosell41 threadrunsell39 threadgosell37 threadgosell35 threadrunsell35 threadgosell33threadrunsell33 threadgosell31threadrunsell31 threadgosell29 threadrunsell29 threadgosell27 threadrunsell27 threadgosell25 threadrunsell25 threadgosell23 threadrunsell21 threadgosell21 threadrunsell19 threadgosell17 threadgosell15 threadrunsell15 threadrunsell13 threadgosell13 threadrunsell11threadgosell11 threadgosell9 threadrunsell9 threadgosell7 threadgosell5threadrunsell5 threadrunsell3 threadgosell1 threadrunsell1

可以看到,有的票卖了两次,有的票就没卖。

4.注意

1.Sleep()函数是使得线程休眠的函数,这个函数不跨平台,仅仅在windows上能用,其他平台使用usleep。

2.在非主线程中不能使用cocos2d-x管理内存的CCObject::retain(),CCObject::release()CCObject::autorelease(),因为CCautoreleasePool不是线程安全的,OPENGL的上下文也不是线程安全的,所以不要再非主线程中使用cocos2d-x的API和UI *** 作。



cocos2dx内存管理与多线程问题:

Cocos2d-x的内存管理采用Objective-C的机制,大喜过望。因为只要坚持Objective-C的原则“谁创建谁释放,谁备份谁释放”的原则即可确保内存使用不易出现BUG。
但是因为本身开放的游戏需要使用到多线程技术,导致测试的时候总是莫名其妙的导致空指针错误。而且是随机出现,纠结了2天无果后,开始怀疑Cocos2d-X的内存本身管理可能存在问题。怀着这样的想法,
一步一步的调试,发现经常出现指针异常的变量总是在调用autorelease后一会就莫名其妙再使用的时候就抛异常。狠下心,在它的析构函数里面断点+Log输出信息。发现对象被释放了。一时也很迷糊,因为对象只是

autorelease,并没有真正释放,是谁导致它释放的?


然后就去看了CCautoreleasePool的源码,发现存在Cocos2d-X的内存管理在多线程的情况下存在如下问题

如图:thread 1和thread 2是独立的两个线程,它们之间存在cpu分配的交叉集,我们在time 1的时候push一个autorelease的自动释放池,在该线程的末尾,即time 3的时候pop它。同理在thread 2的线程里面,在time 2的时候push一个自动释放池,在time 4的时候释放它,即Pop.
此时我们假设在thread 2分配得到cpu的时候有一个对象obj自动释放,即obj-autorelease().那么在time 3的时候会发生是么事情呢?
答案很简单,就是obj在time 3的时候就被释放了,而我们期望它在time 4的时候才释放。所以就导致我上面说的,在多线程下面,cocos2d-x的autorelease变量会发生莫名其妙的指针异常。
解决办法:在PoolManager给每个线程根据pthread_t的线程ID生成一个CCArray的stack的嵌套管理自动释放池。源码如下
所以我在Push的时候根据当前线程的pthread_t的线程ID生成一个CCArray的stack来存储该线程对应的autoreleasepool的嵌套对象
源码如下

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

//--------------------------------------------------------------------

//

// CCPoolManager

//

//--------------------------------------------------------------------


/////【diff - begin】- by layne//////


CCPoolManager* CCPoolManager::sharedPoolManager()

{

if (s_pPoolManager == NulL)

{

s_pPoolManager = new CCPoolManager();

}

return s_pPoolManager;

}


voID CCPoolManager::purgePoolManager()

{

CC_SAFE_DELETE(s_pPoolManager);

}


CCPoolManager::CCPoolManager()

{

// m_pReleasePoolStack = new CCArray();

// m_pReleasePoolStack->init();

// m_pCurReleasePool = 0;


m_pReleasePoolMultiStack = new CCDictionary();

}


CCPoolManager::~CCPoolManager()

{


// finalize();


// // we only release the last autorelease pool here

// m_pCurReleasePool = 0;

// m_pReleasePoolStack->removeObjectAtIndex(0);

//

// CC_SAFE_DELETE(m_pReleasePoolStack);


finalize();


CC_SAFE_DELETE(m_pReleasePoolMultiStack);

}


voID CCPoolManager::finalize()

{

if(m_pReleasePoolMultiStack->count() > 0)

{

//CCautoreleasePool* pReleasePool;

CCObject* pkey = NulL;

CCARRAY_FOREACH(m_pReleasePoolMultiStack->allKeys(),pkey)

{

if(!pkey)

break;

CCInteger *key = (CCInteger*)pkey;

CCArray *poolStack = (CCArray *)m_pReleasePoolMultiStack->objectForKey(key->getValue());

CCObject* pObj = NulL;

CCARRAY_FOREACH(poolStack,pObj)

{

if(!pObj)

break;

CCautoreleasePool* pPool = (CCautoreleasePool*)pObj;

pPool->clear();

}

}

}

}


voID CCPoolManager::push()

{

// CCautoreleasePool* pPool = new CCautoreleasePool(); //ref = 1

// m_pCurReleasePool = pPool;

//

// m_pReleasePoolStack->addobject(pPool); //ref = 2

//

// pPool->release(); //ref = 1


pthread_mutex_lock(&m_mutex);


CCArray* pCurReleasePoolStack = getCurReleasePoolStack();

CCautoreleasePool* pPool = new CCautoreleasePool(); //ref = 1

pCurReleasePoolStack->addobject(pPool); //ref = 2

pPool->release(); //ref = 1


pthread_mutex_unlock(&m_mutex);

}


voID CCPoolManager::pop()

{

// if (! m_pCurReleasePool)

// {

// return;

// }

//

// int nCount = m_pReleasePoolStack->count();

//

// m_pCurReleasePool->clear();

//

// if(nCount > 1)

// {

// m_pReleasePoolStack->removeObjectAtIndex(nCount-1);

//

// // if(nCount > 1)

// // {

// // m_pCurReleasePool = m_pReleasePoolStack->objectAtIndex(nCount - 2);

// // return;

// // }

// m_pCurReleasePool = (CCautoreleasePool*)m_pReleasePoolStack->objectAtIndex(nCount - 2);

// }

//

// /*m_pCurReleasePool = NulL;*/


pthread_mutex_lock(&m_mutex);


CCArray* pCurReleasePoolStack = getCurReleasePoolStack();

CCautoreleasePool* pCurReleasePool = getCurReleasePool();

if (pCurReleasePoolStack && pCurReleasePool)

{

int nCount = pCurReleasePoolStack->count();


pCurReleasePool->clear();


if(nCount > 1)

{

pCurReleasePoolStack->removeObject(pCurReleasePool);

}

}


pthread_mutex_unlock(&m_mutex);

}


voID CCPoolManager::removeObject(CCObject* pObject)

{

// CCAssert(m_pCurReleasePool,"current auto release pool should not be null");

//

// m_pCurReleasePool->removeObject(pObject);


pthread_mutex_lock(&m_mutex);

CCautoreleasePool* pCurReleasePool = getCurReleasePool();

CCAssert(pCurReleasePool,"current auto release pool should not be null");


pCurReleasePool->removeObject(pObject);

pthread_mutex_unlock(&m_mutex);

}


voID CCPoolManager::addobject(CCObject* pObject)

{

// getCurReleasePool()->addobject(pObject);


pthread_mutex_lock(&m_mutex);

CCautoreleasePool* pCurReleasePool = getCurReleasePool(true);

CCAssert(pCurReleasePool,"current auto release pool should not be null");


pCurReleasePool->addobject(pObject);

pthread_mutex_unlock(&m_mutex);

}


CCArray* CCPoolManager::getCurReleasePoolStack()

{

CCArray* pPoolStack = NulL;

pthread_t tID = pthread_self();

if(m_pReleasePoolMultiStack->count() > 0)

{

pPoolStack = (CCArray*)m_pReleasePoolMultiStack->objectForKey((int)tID);

}


if (!pPoolStack) {

pPoolStack = new CCArray();

m_pReleasePoolMultiStack->setobject(pPoolStack,(int)tID);

pPoolStack->release();

}


return pPoolStack;

}


CCautoreleasePool* CCPoolManager::getCurReleasePool(bool autocreate)

{

// if(!m_pCurReleasePool)

// {

// push();

// }

//

// CCAssert(m_pCurReleasePool,"current auto release pool should not be null");

//

// return m_pCurReleasePool;


CCautoreleasePool* pReleasePool = NulL;



CCArray* pPoolStack = getCurReleasePoolStack();

if(pPoolStack->count() > 0)

{

pReleasePool = (CCautoreleasePool*)pPoolStack->lastObject();

}


if (!pReleasePool && autocreate) {

CCautoreleasePool* pPool = new CCautoreleasePool(); //ref = 1

pPoolStack->addobject(pPool); //ref = 2

pPool->release(); //ref = 1


pReleasePool = pPool;

}


return pReleasePool;

}


/////【diff - end】- by layne//////



代码下载地址:https://github.com/kaitiren/pthread-test-for-cocos2dx

总结

以上是内存溢出为你收集整理的cocos2dx多线程以及线程同步 与 cocos2dx内存管理与多线程问题全部内容,希望文章能够帮你解决cocos2dx多线程以及线程同步 与 cocos2dx内存管理与多线程问题所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/1008013.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-22
下一篇 2022-05-22

发表评论

登录后才能评论

评论列表(0条)

保存