浅谈 linux 多线程编程和 windows 多线程编程的异同

很早之前就想写写linux下多线程编程和windows下的多线程编程了,可是每当写时又不知道从哪一个地方写起,怎样把本身知道的东西都写出来,下面我就谈谈linux多线程及线程同步,并将它和windows的多线程进行比较,看看他们之间有什么相同点和不一样的地方。linux

其实最开始我是搞windows下编程的,包括windows编程,windows 驱动,包括usb驱动,ndis驱动,pci驱动,1394驱动等等,同时也一条龙服务,作windows下的应用程序开发,后面慢慢的我又对linux 开发产生比较深的兴趣和爱好,就转到搞linux开发了。在接下来的我还会写一些博客,主要是写linux编程和windows编程的区别吧,如今想写的 是linux下usb驱动和windows下usb驱动开发的区别,这些都是后话,等我将linux多线程和windows多线程讲解完后,我再写一篇 usb驱动,谈谈windows 和linux usb驱动的东东。好了,言归正传。开始将多线程了。编程

首先咱们讲讲为何要采用多线程编程,其实并非全部的程序都必须采用多线程,有些时候采用多线程,性能尚未单线程好。因此咱们要搞清楚,何时采用多线程。采用多线程的好处以下:windows

(1)由于多线程彼此之间采用相同的地址空间,共享大部分的数据,这样和多进程相比,代价比较节俭,由于多进程的话,启动新的进程必须分配给它独立的地址空间,这样须要数据表来维护代码段,数据段和堆栈段等等。多线程

(2)多线程和多进程相比,一个明显的优势就是线程之间的通讯了,对不一样进程来讲,它们具备独立的数据空间,要进行数据的传递只能经过通讯的方式进 行,这种方式不只费时,并且很不方便。可是对于多线程就不同了。他们之间能够直接共享数据,好比最简单的方式就是共享全局变量。可是共享所有变量也要注 意哦,呵呵,必须注意同步,否则后果你知道的。呵呵。wordpress

(3)在多cpu的状况下,不一样的线程能够运行不一样的cpu下,这样就彻底并行了。函数

反正我以为在这种状况下,采用多线程比较理想。好比说你要作一个任务分2个步骤,你为提升工做效率,你能够多线程技术,开辟2个线程,第一个线程就 作第一步的工做,第2个线程就作第2步的工做。可是你这个时候要注意同步了。由于只有第一步作完才能作第2步的工做。这时,咱们能够采用同步技术进行线程 之间的通讯。post

针对这种状况,咱们首先讲讲多线程之间的通讯,在windows平台下,多线程之间通讯采用的方法主要有:性能

(1)共享全局变量,这种方法是最容易想到的,呵呵,那就首先讲讲吧,好比说吧,上面的问题,第一步要向第2步传递收据,咱们能够之间共享全局变 量,让两个线程之间传递数据,这时主要考虑的就是同步了,由于你后面的线程在对数据进行操做的时候,你第一个线程又改变了数据的内容,你不一样步保护,后果 很严重的。你也知道,这种状况就是读脏数据了。在这种状况下,咱们最容易想到的同步方法就是设置一个bool flag了,好比说在第2个线程尚未用完数据前,第一个线程不能写入。有时在2个线程所需的时间不相同的时候,怎样达到最大效率的同步,就比较麻烦了。 我们能够多开几个缓冲区进行操做。就像生产者消费者同样了。若是是2个线程一直在跑的,因为时间不一致,缓冲区早晚会溢出的。在这种状况下就要考虑了,是 不让数据写入仍是让数据覆盖掉老的数据,这时候就要具体问题具体分析了。就此打住,呵呵。就是用bool变量控制同步,linux 和windows是同样的。测试

既然讲道了这里,就再讲讲其它同步的方法。一样 针对上面的这个问题,共享全局变量同步问题。除了采用bool变量外,最容易想到的方法就是互斥量了。呵呵,也就是传说中的加锁了。windows下加锁 和linux下加锁是相似的。采用互斥量进行同步,要想进入那段代码,就先必须得到互斥量。优化

linux上互斥量的函数是:

windows下互斥量的函数有:createmutex 建立一个互斥量,而后就是得到互斥量waitforsingleobject函数,用完了就释放互斥量ReleaseMutex(hMutex),当减到 0的时候 内核会才会释放其对象。下面是windows下与互斥的几个函数原型。

HANDLE WINAPI CreateMutex(
__in LPSECURITY_ATTRIBUTES lpMutexAttributes,
__in BOOL bInitialOwner,
__in LPCTSTR lpName
);
能够可用来建立一个有名或无名的互斥量对象
第一参数 能够指向一个结构体SECURITY_ATTRIBUTES 通常能够设为null;
第二参数 指当时的函数是否是感应感应状态 FALSE为当前拥有者不会建立互斥
第三参数 指明是不是有名的互斥对象 若是是无名 用null就好。

DWORD WINAPI WaitForSingleObject(

__in HANDLE hHandle,

__in DWORD dwMilliseconds

);

第一个是 建立的互斥对象的句柄。第二个是 表示将在多少时间以后返回 若是设为宏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-set Event 为表明。

第2种模型则是Linux平台的pthread所采用的模型,当条件变量置位(signaled)之后,即便当前没有任何线程在等待,其状态也会恢复为复位(unsignaled)状态。

条件变量在Linux平台上的这种模型很难说好坏,在实际应用中,咱们能够对

代码稍加改进就能够避免这种差别的发生。因为这种差别只会发生在触发没有被线程等待在条件变量的时刻,所以咱们只须要掌握好触发的时机便可。最简单的作法是增长一个计数器记录等待线程的个数,在决定触发条件变量前检查该变量便可。

示例 使用 pthread_cond_wait() 和 pthread_cond_signal()

pthread_mutex_t count_lock;

pthread_cond_t count_nonzero;

unsigned count;

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条件返回时互斥锁的解锁问题

extern int pthread_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_t mutex = PTHREAD_MUTEX_INITIALIZER; /*初始化互斥锁*/

pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //初始化条件变量

void *thread1(void *);

void *thread2(void *);

int i=1;

int main(void)

{

pthread_t t_a;

pthread_t t_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("IN one\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("Up Mutex\n");

sleep(3);

}

}

void *thread2(void *junk)

{

while(i<9)

{

printf("IN two \n");

pthread_mutex_lock(&mutex);

if(i%3!=0)

pthread_cond_wait(&cond,&mutex);/*等待*/

printf("thread2:%d\n",i);

pthread_mutex_unlock(&mutex);

printf("Down Mutex\n");

sleep(3);

}

}

输出以下:

IN one

thead1:1

Up Mutex

IN two

IN one

thead1:2

Up Mutex

IN one

thread2:3

Down Mutex

Up Mutex

IN one

thead1:4

Up Mutex

IN two

IN one

thead1:5

Up Mutex

IN one

Up Mutex

thread2:6

Down Mutex

IN two

thread2:6

Down Mutex

IN one

thead1:7

Up Mutex

IN one

thead1:8

Up Mutex

IN two

IN one

Up Mutex

thread2:9

Down Mutex

注意蓝色的地方,有2个thread2:6,其实当这个程序多执行几回,i=3和i=6时有可能多打印几个,这里就是竞争锁形成的了。

(4)另外要注意的Pthread_cond_timedwait等待的是绝对时间,这个和WaitForSingleObject是不一样的,Pthread_cond_timedwait在网上也有讨论。以下:这个问题比较经典,我把它搬过来。

thread_a :
pthread_mutex_lock(&mutex);
//do something
pthread_mutex_unlock(&mutex)

thread_b:

pthread_mutex_lock(&mutex);
//do something
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, const struct timespec *abstime) -- This function is a time-based variant of pthread_cond_wait. It waits up to abstime amount of time for cv to be notified. If abstime elapses before cv is notified, the function returns back to the caller with an ETIME result, signifying that a timeout has occurred. Even in the case of timeouts, the external_mutex will be locked when pthread_cond_timedwait returns.

(2) 2.1 pthread_cond_timedwait行为和pthread_cond_wait同样,在返回的时候都要再次lock mutex.
2 .2pthread_cond_timedwait所谓的若是没有等到条件变量,超时就返回,并不确切。
若是pthread_cond_timedwait超时到了,可是这个时候不能lock临界区,pthread_cond_timedwait并不会当即 返回,可是在pthread_cond_timedwait返回的时候,它仍在临界区中,且此时返回值为ETIMEDOUT。

关于pthread_cond_timedwait超时返回的问题,我也认同观点2。

附录:

int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict_attr,void*(*start_rtn)(void*),void *restrict arg);

返回值:若成功则返回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

线程等待:

int pthread_join(pthread_t tid,void **rval_ptr)

函数pthread_join用来等待一个线程的结束。函数原型为:

extern int pthread_join __P (pthread_t __th, void **__thread_return);

第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,它能够用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。

对于windows线程的建立东西,就不列举了,msdn上 一搜就出来了。呵呵。今天就讲到这里吧,但愿是抛砖引玉,你们一块儿探讨,呵呵。部份内容我也是参考internet的,特此对原做者表示感谢!

相关文章
相关标签/搜索