浅谈 linux 多线程编程和 windows 多线程编程的异同.docx
- 文档编号:16116354
- 上传时间:2023-07-10
- 格式:DOCX
- 页数:13
- 大小:95.20KB
浅谈 linux 多线程编程和 windows 多线程编程的异同.docx
《浅谈 linux 多线程编程和 windows 多线程编程的异同.docx》由会员分享,可在线阅读,更多相关《浅谈 linux 多线程编程和 windows 多线程编程的异同.docx(13页珍藏版)》请在冰点文库上搜索。
浅谈linux多线程编程和windows多线程编程的异同
浅谈linux多线程编程和windows多线程编程的异同
很早以前就想写写linux下多线程编程和windows下的多线程编程了,但是每当写时又不知道从哪个地方写起,怎样把自己知道的东西都写出来,下面我就谈谈linux多线程及线程同步,并将它和windows的多线程进行比较,看看他们之间有什么相同点和不同的地方。
其实最开始我是搞windows下编程的,包括windows编程,windows驱动,包括usb驱动,ndis驱动,pci驱动,1394驱动等等,同时也一条龙服务,做windows下的应用程序开发,后面慢慢的我又对linux开发产生比较深的兴趣和爱好,就转到搞linux开发了。
在接下来的我还会写一些博客,主要是写linux编程和windows编程的区别吧,现在想写的是linux下usb驱动和windows下usb驱动开发的区别,这些都是后话,等我将linux多线程和windows多线程讲解完后,我再写一篇usb驱动,谈谈windows和linuxusb驱动的东东。
好了,言归正传。
开始将多线程了。
首先我们讲讲为什么要采用多线程编程,其实并不是所有的程序都必须采用多线程,有些时候采用多线程,性能还没有单线程好。
所以我们要搞清楚,什么时候采用多线程。
采用多线程的好处如下:
(1)因为多线程彼此之间采用相同的地址空间,共享大部分的数据,这样和多进程相比,代价比较节俭,因为多进程的话,启动新的进程必须分配给它独立的地址空间,这样需要数据表来维护代码段,数据段和堆栈段等等。
(2)多线程和多进程相比,一个明显的优点就是线程之间的通信了,对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。
但是对于多线程就不一样了。
他们之间可以直接共享数据,比如最简单的方式就是共享全局变量。
但是共享全部变量也要注意哦,呵呵,必须注意同步,不然后果你知道的。
呵呵。
(3)在多cpu的情况下,不同的线程可以运行不同的cpu下,这样就完全并行了。
反正我觉得在这种情况下,采用多线程比较理想。
比如说你要做一个任务分2个步骤,你为提高工作效率,你可以多线程技术,开辟2个线程,第一个线程就做第一步的工作,第2个线程就做第2步的工作。
但是你这个时候要注意同步了。
因为只有第一步做完才能做第2步的工作。
这时,我们可以采用同步技术进行线程之间的通信。
针对这种情况,我们首先讲讲多线程之间的通信,在windows平台下,多线程之间通信采用的方法主要有:
(1)共享全局变量,这种方法是最容易想到的,呵呵,那就首先讲讲吧,比如说吧,上面的问题,第一步要向第2步传递收据,我们可以之间共享全局变量,让两个线程之间传递数据,这时主要考虑的就是同步了,因为你后面的线程在对数据进行操作的时候,你第一个线程又改变了数据的内容,你不同步保护,后果很严重的。
你也知道,这种情况就是读脏数据了。
在这种情况下,我们最容易想到的同步方法就是设置一个boolflag了,比如说在第2个线程还没有用完数据前,第一个线程不能写入。
有时在2个线程所需的时间不相同的时候,怎样达到最大效率的同步,就比较麻烦了。
咱们可以多开几个缓冲区进行操作。
就像生产者消费者一样了。
如果是2个线程一直在跑的,由于时间不一致,缓冲区迟早会溢出的。
在这种情况下就要考虑了,是不让数据写入还是让数据覆盖掉老的数据,这时候就要具体问题具体分析了。
就此打住,呵呵。
就是用bool变量控制同步,linux和windows是一样的。
既然讲道了这里,就再讲讲其它同步的方法。
同样针对上面的这个问题,共享全局变量同步问题。
除了采用bool变量外,最容易想到的方法就是互斥量了。
呵呵,也就是传说中的加锁了。
windows下加锁和linux下加锁是类似的。
采用互斥量进行同步,要想进入那段代码,就先必须获得互斥量。
linux上互斥量的函数是:
windows下互斥量的函数有:
createmutex创建一个互斥量,然后就是获得互斥量waitforsingleobject函数,用完了就释放互斥量ReleaseMutex(hMutex),当减到0的时候内核会才会释放其对象。
下面是windows下与互斥的几个函数原型。
HANDLEWINAPICreateMutex(
__inLPSECURITY_ATTRIBUTESlpMutexAttributes,
__inBOOLbInitialOwner,
__inLPCTSTRlpName
);
可以可用来创建一个有名或无名的互斥量对象
第一参数可以指向一个结构体SECURITY_ATTRIBUTES 一般可以设为null;
第二参数指当时的函数是不是感应感应状态FALSE为当前拥有者不会创建互斥
第三参数指明是否是有名的互斥对象如果是无名用null就好。
DWORDWINAPIWaitForSingleObject(
__inHANDLEhHandle,
__inDWORDdwMilliseconds
);
第一个是创建的互斥对象的句柄。
第二个是表示将在多少时间之后返回如果设为宏INFINITE则不会返回直到用户自己定义返回。
对于linux操作系统,互斥也是类似的,只是函数不同罢了。
在linux下,和互斥相关的几个函数也要闪亮登场了。
pthread_mutex_init函数:
初始化一个互斥锁;
pthread_mutex_destroy函数:
注销一个互斥锁;
pthread_mutex_lock函数:
加锁,如果不成功,阻塞等待;
pthread_mutex_unlock函数:
解锁;
pthread_mutex_trylock函数:
测试加锁,如果不成功就立即返回,错误码为EBUSY;
至于这些函数的用法,google上一搜,就出来了,呵呵,在这里不多讲了。
windows下还有一个可以用来保护数据的方法,也是线程同步的方式
就是临界区了。
临界区和互斥类似。
它们之间的区别是,临界区速度快,但是它只能用来同步同一个进程内的多个线程。
临界区的获取和释放函数如下:
EnterCriticalSection()进入临界区;LeaveCriticalSection()离开临界区。
对于多线程共享内存的东东就讲到这里了。
(2)采用消息机制进行多线程通信和同步,windows下面的的消息机制的函数用的多的就是postmessage了。
Linux下的消息机制,我用的较少,就不在这里说了,如果谁熟悉的,也告诉我,呵呵。
(3)windows下的另外一种线程通信方法就是事件和信号量了。
同样针对我开始举得例子,2个线程同步,他们之间传递信息,可以采用事件(Event)或信号量(Semaphore),比如第一个线程完成生产的数据后,就必须告诉第2个线程,他已经把数据准备好了,你可以来取走了。
第2个线程就把数据取走。
呵呵,这里可以采用消息机制,当第一个线程准备好数据后,就直接postmessage给第2个线程,按理说采用postmessage一个线程就可以搞定这个问题了。
呵呵,不是重点,省略不讲了。
对于linux,也有类似的方法,就是条件变量了,呵呵,这里windows和linux就有不同了。
要特别讲讲才行。
对于windows,采用事件和信号量同步时候,都会使用waitforsingleobject进行等待的,这个函数的第一个参数是一个句柄,在这里可以是Event句柄,或Semaphore句柄,第2个参数就是等待的延迟,最终等多久,单位是ms,如果这个参数为INFINITE,那么就是无限等待了。
释放信号量的函数为ReleaseSemaphore();释放事件的函数为SetEvent。
当然使用这些东西都要初始化的。
这里就不讲了。
Msdn一搜,神马都出来了,呵呵。
神马都是浮云!
对于linux操作系统,是采用条件变量来实现类似的功能的。
Linux的条件变量一般都是和互斥锁一起使用的,主要的函数有:
pthread_mutex_lock,
pthread_mutex_unlock,
pthread_cond_init
pthread_cond_signal
pthread_cond_wait
pthread_cond_timewait
为了和windows操作系统进行对比,我用以下表格进行比较:
对照以上表格,总结如下:
(1)Pthread_cleanup_push,Pthread_cleanup_pop:
这一对函数push和pop的作用是当出现异常退出时,做一些清除操作,即当在push和pop函数之间异常退出,包括调用pthread_exit退出,都会执行push里面的清除函数,如果有多个push,注意是是栈,先执行后面的那个函数,在执行前面的函数,但是注意当在这2个函数之间通过return退出的话,执不执行push后的函数就看pop函数中的参数是不是为0了。
还有当没有异常退出时,等同于在这里面return退出的情况,即:
当pop函数参数不为0时,执行清除操作,当pop函数参数为0时,不执行push函数中的清除函数。
(2)linux的pthread_cond_signal和SetEvent的不同点
Pthread_cond_singal释放信号后,当没有Pthread_cond_wait,信号马上复位了,这点和SetEvent不同,SetEvent是不会复位的。
详解如下:
条件变量的置位和复位有2种常用模型:
第一种模型是当条件变量置位时(signaled)以后,如果当前没有线程在等待,其状态会保持为置位(signaled),直到有等待的线程进入被触发,其状态才会变为unsignaled,这种模型以采用Windows平台上的Auto-setEvent为代表。
第2种模型则是Linux平台的pthread所采用的模型,当条件变量置位(signaled)以后,即使当前没有任何线程在等待,其状态也会恢复为复位(unsignaled)状态。
条件变量在Linux平台上的这种模型很难说好坏,在实际应用中,我们可以对
代码稍加改进就可以避免这种差异的发生。
由于这种差异只会发生在触发没有被线程等待在条件变量的时刻,因此我们只需要掌握好触发的时机即可。
最简单的做法是增加一个计数器记录等待线程的个数,在决定触发条件变量前检查该变量即可。
示例使用pthread_cond_wait()和pthread_cond_signal()
pthread_mutex_tcount_lock;
pthread_cond_tcount_nonzero;
unsignedcount;
decrement_count()
{
pthread_mutex_lock(&count_lock);
while(count==0)
pthread_cond_wait(&count_nonzero,&count_lock);
count=count-1;
pthread_mutex_unlock(&count_lock);
}
increment_count()
{
pthread_mutex_lock(&count_lock);
if(count==0)
pthread_cond_signal(&count_nonzero);
count=count+1;
pthread_mutex_unlock(&count_lock);
}
(3)注意Pthread_cond_wait条件返回时互斥锁的解锁问题
externintpthread_cond_wait__P((pthread_cond_t*__cond,pthread_mutex_t*__mutex));
调用这个函数时,线程解开mutex指向的锁并被条件变量cond阻塞。
线程可以被函数pthread_cond_signal和函数pthread_cond_broadcast唤醒线程被唤醒后,它将重新检查判断条件是否满足,如果还不满足,一般说来线程应该仍阻塞在这里,被等待被下一次唤醒。
如果在多线程中采用pthread_cond_wait来等待时,会首先释放互斥锁,当等待的信号到来时,再次获得互斥锁,因此在之后要注意手动解锁。
举例如下:
#include
#include
#include
pthread_mutex_tmutex=PTHREAD_MUTEX_INITIALIZER;/*初始化互斥锁*/
pthread_cond_tcond=PTHREAD_COND_INITIALIZER;//初始化条件变量
void*thread1(void*);
void*thread2(void*);
inti=1;
intmain(void)
{
pthread_tt_a;
pthread_tt_b;
pthread_create(&t_a,NULL,thread1,(void*)NULL);/*创建进程t_a*/
pthread_create(&t_b,NULL,thread2,(void*)NULL);/*创建进程t_b*/
pthread_join(t_b,NULL);/*等待进程t_b结束*/
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
exit(0);
}
void*thread1(void*junk)
{
for(i=1;i<=9;i++)
{
printf("INone\n");
pthread_mutex_lock(&mutex);//
if(i%3==0)
pthread_cond_signal(&cond);/*,发送信号,通知t_b进程*/
else
printf("thead1:
%d\n",i);
pthread_mutex_unlock(&mutex);//*解锁互斥量*/
printf("UpMutex\n");
sleep(3);
}
}
void*thread2(void*junk)
{
while(i<9)
{
printf("INtwo\n");
pthread_mutex_lock(&mutex);
if(i%3!
=0)
pthread_cond_wait(&cond,&mutex);/*等待*/
printf("thread2:
%d\n",i);
pthread_mutex_unlock(&mutex);
printf("DownMutex\n");
sleep(3);
}
}
输出如下:
INone
thead1:
1
UpMutex
INtwo
INone
thead1:
2
UpMutex
INone
thread2:
3
DownMutex
UpMutex
INone
thead1:
4
UpMutex
INtwo
INone
thead1:
5
UpMutex
INone
UpMutex
thread2:
6
DownMutex
INtwo
thread2:
6
DownMutex
INone
thead1:
7
UpMutex
INone
thead1:
8
UpMutex
INtwo
INone
UpMutex
thread2:
9
DownMutex
注意蓝色的地方,有2个thread2:
6,其实当这个程序多执行几次,i=3和i=6时有可能多打印几个,这里就是竞争锁造成的了。
(4)另外要注意的Pthread_cond_timedwait等待的是绝对时间,这个和WaitForSingleObject是不同的,Pthread_cond_timedwait在网上也有讨论。
如下:
这个问题比较经典,我把它搬过来。
thread_a:
pthread_mutex_lock(&mutex);
//dosomething
pthread_mutex_unlock(&mutex)
thread_b:
pthread_mutex_lock(&mutex);
//dosomething
pthread_cond_timedwait(&cond,&mutex,&tm);
pthread_mutex_unlock(&mutex)
有如上两个线程thread_a,thread_b,现在如果a已经进入了临界区,而b同时超时了,那么b会从pthread_cond_timedwait返回吗?
如果能返回,那岂不是a,b都在临界区?
如果不能返回,那pthread_cond_timedwait的定时岂不是就不准了?
大家讨论有价值的2点如下:
(1)pthread_cond_timedwait(pthread_cond_t*cv,pthread_mutex_t*external_mutex,conststructtimespec*abstime)--Thisfunctionisatime-basedvariantofpthread_cond_wait.Itwaitsuptoabstimeamountoftimeforcvtobenotified.Ifabstimeelapsesbeforecvisnotified,thefunctionreturnsbacktothecallerwithanETIMEresult,signifyingthatatimeouthasoccurred.Eveninthecaseoftimeouts,theexternal_mutexwillbelockedwhenpthread_cond_timedwaitreturns.
(2)2.1pthread_cond_timedwait行为和pthread_cond_wait一样,在返回的时候都要再次lockmutex.
2.2pthread_cond_timedwait所谓的如果没有等到条件变量,超时就返回,并不确切。
如果pthread_cond_timedwait超时到了,但是这个时候不能lock临界区,pthread_cond_timedwait并不会立即返回,但是在pthread_cond_timedwait返回的时候,它仍在临界区中,且此时返回值为ETIMEDOUT。
关于pthread_cond_timedwait超时返回的问题,我也认同观点2。
附录:
intpthread_create(pthread_t*restricttidp,constpthread_attr_t*restrict_attr,void*(*start_rtn)(void*),void*restrictarg);
返回值:
若成功则返回0,否则返回出错编号
返回成功时,由tidp指向的内存单元被设置为新创建线程的线程ID。
attr参数用于制定各种不同的线程属性。
新创建的线程从start_rtn函数的地址开始运行,该函数只有一个无指针参数arg,如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg的参数传入。
linux下用C开发多线程程序,Linux系统下的多线程遵循POSIX线程接口,称为pthread。
由restrict修饰的指针是最初唯一对指针所指向的对象进行存取的方法,仅当第二个指针基于第一个时,才能对对象进行存取。
对对象的存取都限定于基于由restrict修饰的指针表达式中。
由restrict修饰的指针主要用于函数形参,或指向由malloc()分配的内存空间。
restrict数据类型不改变程序的语义。
编译器能通过作出restrict修饰的指针是存取对象的唯一方法的假设,更好地优化某些类型的例程。
第一个参数为指向线程标识符的指针。
第二个参数用来设置线程属性。
第三个参数是线程运行函数的起始地址。
第四个参数是运行函数的参数。
因为pthread不是linux系统的库,所以在编译时注意加上-lpthread参数,以调用静态链接库。
终止线程:
如果在进程中任何一个线程中调用exit或_exit,那么整个进行会终止,线程正常的退出方式有:
(1)线程从启动例程中返回(return)
(2)线程可以被另一个进程终止(kill);
(3)线程自己调用pthread_exit函数
#include
pthread_exit
线程等待:
intpthread_join(pthread_ttid,void**rval_ptr)
函数pthread_join用来等待一个线程的结束。
函数原型为:
externintpthread_join__P(pthread_t__th,void**__thread_return);
第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。
这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。
对于windows线程的创建东西,就不列举了,msdn上一搜就出来了。
呵呵。
今天就讲到这里吧,希望是抛砖引玉,大家一起探讨,呵呵。
部分内容我也是参考internet的,特此对原作者表示感谢!
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 浅谈 linux 多线程编程和 windows 多线程编程的异同 多线程 编程 异同