多线程原理、线程安全函数和多线程程序需要注意的问题

1.,例如下载过程中进度条改变,读取文件的时候显示结果。
2.,提高CPU使用效率,。

多线程的主要是需要处理大量的IO操作或者处理的情况需要花大量的时间等等,比如读写文件,网络数据接收,视频图像的采集,处理显示保存等中使用

但也不是都使用多线程,因为多线程过多的线程一般会导致数据共享问题,太多多线程切换也是会影响性能的,所以一般不须采用多线程的不用多线程效果更好。


线程是分配CPU资源的最小单位,单CPU多线程是时间轮片的切换,多CPU可以真正的做到多CPU同时工作

2.多线程具体的实现
。 全局变量、静态数据、堆内存、同步锁变量、组件对象)由进程中的所有线程共享(共有资源)使用这些公共资源的线程必须同步。Win32 提供了几种同步资源的方式,包括
说一下线程自己的堆栈问题。

 

这个多线程的例子应该很明了了,主线程做自己的事情,生成2个子线程,task1为分离,任其自生自灭,而task2还是继续送外卖,需要等待返回。(因该还记得前面说过僵尸进程吧,线程也是需要等待的。如果不想等待,就设置线程为分离线程)。
1)自生自灭类型:

pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);   //设置attr结构为分离  
pthread_create(&pid1, &attr, task1, NULL);         //创建线程,返回线程号给pid1,线程属性设置为attr的属性,线程函数入口为task1,参数为NULL  

2)继续等待类型:
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);  
pthread_create(&pid2, &attr, task2, NULL);  
//前台工作  
ret=pthread_join(pid2, &p);         //等待pid2返回,返回值赋给p  
printf("after pthread2:ret=%d,p=%d/n", ret,(int)p);  

三、线程安全函数:

时,收到信号,于是暂停目前正在执行的函数,转到信号处理函数,而这个信号处理函数的执行过程中,又恰恰也会进入到刚刚执行的函数,这样便发生了所谓的重入。此时如果能够正确的运行,而且处理完成后,之前暂停的也能够正确运行,则说明它是可重入的, 反复调用都得到正确的结果。

 

 

、不在函数内部使用静态或全局数据 2 3、不调用不可重入函数。

 函数的条件:

如果是可重入的函数那么是线程安全的,如果是线程不安全的函数,那么需要通过对共享数据(全局变量/静态变量,文件对象,堆内存)
线程函数和线程调用的函数要求细则:

多线程中调用,如果是一个线程来回(回调)反复调用的那么要求是可重入的函数;如果是没有回调的普通线程调用的函数那么需要是线程安全的函数。

如果对可重入函数,线程安全函数不做处理,那么会导致不可预料的后果:


所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不 用考虑同步的问题。
MFC和STL都不是线程安全的. 怎样才能设计出线程安全的类或者接口呢?
1)如果接口中访问的数据都属于只读操作,那么这 样的接口也是线程安全的
3)如果多个接口之间有共享数据,而且有读有写的话, 会维护一份自己的栈和寄存器信息,但是系统一般对于线程安全的函数是可以调用的,线程不安全的函数是会运行时报错的(例如一个工作线程调用函数处理一个界面线程的控件, 线程间不安全函数调用失败原因:

因为控件是窗口,窗口是和显示器驱动相关的,因此窗口对象的写不是线程安全的,所以window的线程安全策略不允许子线程中对窗口这种线程不安全的对象进行写操作。最好的办法是发送消息,通知主线程更新控件内容。

MFC子线程中可以用发送消息给主线程实现对控件的操作,主线程通过消息映射函数,收到消息更新界面信息。
.NET中可以通过委托实现子线程对主线程控件的操作。
cocos2dx中是通过CPP引擎层主线程的更新,设置进度条百分比,然后回调到lua里面的函数实现界面控件的更新,而下载和解压线程是子线程,下载 解压子线程通过发送消息到更新信息队列,主线程通过互斥锁实现访问更新队列。也就是通过子线程发送消息,主线程每帧去无阻塞的取消息,如果取到消息那么回 调实现界面的更新。
cocos2dx中是纹理读取,xml/json文件读取,网络下载,网络消息接收中使用了多线程。

、多线程的程序设计应该注意的内容
1、尽量少的使用全局变量、static变量做共享数据,尽量使用参数传递对象。被参数传递的对象,应该只包括必需的成员变量。所谓必需的成员变量,就是 必定会被多线程操作的。 很多人图省事,会把this指针(可能是任意一个对象指针)当作线程参数传递,致使线程内部有过多的操作权限,对this中的参数 任意妄为。整个程序由一个人完成,可能会非常注意,不会出错,但只要一转手,程序就会面目全非。当两个线程同时操作一个成员变量的时候,程序就开始崩溃 了,更糟的是,这种错误很难被重现。(我就在郁闷这个问题,我们是几个人,把程序编成debug版,经过数天使用,才找到错误。而找到错误只是开始,因为 你要证明这个bug被修改成功了,也非常困难。)其实,线程间数据交互大多是单向的,在线程回调函数入口处,尽可能的将传入的数据备份到局部变量中(当 然,用于线程间通讯的变量不能这么处理),以后只对局部变量做处理,可以很好的解决这种问题。
2、在MFC中请慎用线程。因为MFC的框架假定你的消息处理都是在主线程中完成的。首先窗口句柄是属于线程的,如果拥有窗口句柄的线程退出了,如果另一 个线程处理这个窗口句柄,系统就会出现问题。而MFC为了避免这种情况的发生,使你在子线程中调用消息(窗口)处理函数时,就会不停的出Assert错 误,烦都烦死你。典型的例子就时CSocket,因为CSocket是使用了一个隐藏窗口实现了假阻塞,所以不可避免的使用了消息处理函数,如果你在子线 程中使用CSocket,你就可能看到assert的弹出了。
3、不要在不同的线程中同时注册COM组件。 两个线程,一个注册1.ocx, 2.ocx, 3.ocx, 4.ocx; 而另一个则注册5.ocx, 6.ocx, 7.ocx, 8.ocx,结果死锁发生了,分别死在FreeLibrary和DllRegisterServer,因为这8个ocx是用MFC中做的,也可能是MFC 的Bug,但DllRegisterServer却死在GetModuleFileName里,而GetModuleFileName则是个API唉!如 果有过客看到,恰巧又知道其原因,请不吝赐教。
4、不要把线程搞的那么复杂。很多初学者,恨不能用上线程相关的所有的函 数,这里互斥,那里等待,一会儿起线程,一会儿关线程的,比起goto语句有过之 而无不及。好的多线程程序,应该是尽量少的使用线程。这句话怎么理解呐,就是说尽量统一一块数据共享区存放数据队列,工作子线程从队列中取数据,处理,再 放回数据,这样才会模块化,对象化;而不是每个数据都起一个工作子线程处理,处理完了就关闭,写的时候虽然直接,等维护起来就累了。
posted @ 2015-11-06 11:13  雅思敏  阅读(884)  评论(0)    收藏  举报