C语⾔多线程实例之pthread的应⽤(在windows下的应⽤(win7))
Pthread是由POSIX提出的⼀套通⽤的线程库,在linux平台下,它被⼴泛的⽀持,⽽windows平台下,却并不被⽀持,⽽pthreads-w32为我们提供了解决⽅案,本⽂我们准备在我们的windows平台下进⾏pthread-w32的安装,在⽹络上有类似的⽂章,但是讲的都是⽐较⽼的平台,在windows8下⽀持并不全⾯,不过可以作为参考。我们在这⾥贴出⼏个⽹址,供参考使⽤。
Windows 7 64bit和Visual Studio 2010下安装及使⽤Pthread-w32 2.8
windows下使⽤pthread库(转)
如果你的是XP系统或者win7 32位系统,那么,那两篇⽂章已经⾜以你完成pthread-w32的安装了。现在,我们开始讲我们的尝试过程。
⼀、安装平台
windows8 64位系统,Microsoft Visual Studio 2012
⼆、pthreads-w32 下载地址
我们这⾥下载最新版本pthreads-w32-2-9-1
下载后解压,可以看到共有三个⽂件夹
我们⽤到的主要是“Pre-built.2”这个⽂件夹下的三个⽂件夹,分别是动态链接库、头⽂件、静态链接库
三、配置头⽂件及静态链接库
这⾥有多种⽅式,我们这⾥只提到我们⽤到的⼀种,总之⽬的是让我们建⽴的⼯程能够到对应的头⽂件、静态库⽂件,以及运⾏时程序能够到动态链接库⽂件。
这⾥,我们直接把头⽂件拷贝到Visual Studio的默认路径的头⽂件中,即把include⽂件夹中的三个⽂件直接拷贝到Visual Studio安装⽬录下VC->include⽂件夹下,例如我将include中⽂件拷贝到的位置是
E:\Program Files\Microsoft Visual Studio 11.0\VC\include
这样,我们就不必每次在项⽬⽤到时都配置⼀遍,特别是在Visual Studio2012 貌似不⽀持全局的头⽂件配置时(不确定,如果谁到了可以告诉我⼀声),这种⽅式对于经常会建⼀些⼩项⽬的⼈来说,相对节省时间。
同样的办法与原因,我们也可以把lib⽂件夹下的内容拷贝到Visual Studio安装⽬录下默认的lib寻路径中,即VC->lib中,例如我将lib⽂件夹下的x64与x86两个⽂件直接拷贝到
E:\Program Files\Microsoft Visual Studio 11.0\VC\lib
的下⾯。
四、配置动态链接库
和头⽂件和静态链接库的配置⽅式相似,我们这⾥将dll⽂件夹的内容放到我们程序能够到的位置,我们的⽅案是
把dll下的x64⽂件夹下的两个⽂件,即pthreadGC2.dll与pthreadVC2.dll拷贝到C:\Windows\System32下(⽤于64位程序的运⾏)
把dll下的x86⽂件夹下的五个⽂件,拷贝到C:\Windows\SysWOW64下(⽤于32位程序的运⾏),注意⼀下,千万不能将这些⽂件拷贝反位置,否则,程序运⾏时会提⽰说不到对应的dll⽂件。这些在⽹上的很多⽂章中都被忽略掉了,所以我们特别提出。
五:注意事项:
1)在linux下编译的时候要加上-lpthread ,如:
gcc -g thread_test.c -o thread_test -lpthread
2)按上述步骤如果出错,则把lib⽂件夹下x86中的⽂件直接拷贝到上⼀级⽬录。我的问题解决了。
六:列两个多线程的实例:
/*mutex.c*/
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
/*全局变量*/
int gnum = 0;
/*互斥量 */
pthread_mutex_t mutex;
/
*声明线程运⾏服务程序*/
static void pthread_func_1 (void);
static void pthread_func_2 (void);
int main (void)
{
/*线程的标识符*/
pthread_t pt_1 = 0;
pthread_t pt_2 = 0;
int ret = 0;
/*互斥初始化*/
pthread_mutex_init (&mutex, NULL);
/
*分别创建线程1、2*/
ret = pthread_create( &pt_1,                  //线程标识符指针
NULL,                  //默认属性
(void *)pthread_func_1,//运⾏函数
NULL);                  //⽆参数
if (ret != 0)
{
perror ("pthread_1_create");
}
ret = pthread_create( &pt_2,                  //线程标识符指针
NULL,                  //默认属性
(void *)pthread_func_2, //运⾏函数
NULL);                  //⽆参数
if (ret != 0)
{
perror ("pthread_2_create");
}
/*等待线程1、2的结束*/
pthread_join (pt_1, NULL);
pthread_join (pt_2, NULL);
printf ("main programme exit!/n");
return0;
}
/*线程1的服务程序*/
static void pthread_func_1 (void)
{
linux下的sleep函数int i = 0;
for( i=0; i<3; i++ ){
printf ("This is pthread_1!/n");
pthread_mutex_lock(&mutex); /*获取互斥锁*/
/*注意,这⾥以防线程的抢占,以造成⼀个线程在另⼀个线程sleep时多次访问互斥资源,所以sleep要在得到互斥锁后调⽤*/    sleep (1);
sleep (1);
/*临界资源*/
gnum++;
printf ("Thread_1 add one to num:%d/n",gnum);
pthread_mutex_unlock(&mutex); /*释放互斥锁*/
}
pthread_exit ( NULL );
}
/*线程2的服务程序*/
static void pthread_func_2 (void)
{
int i = 0;
for( i=0; i<5; i++ )  {
printf ("This is pthread_2!/n");
pthread_mutex_lock(&mutex); /*获取互斥锁*/
/*注意,这⾥以防线程的抢占,以造成⼀个线程在另⼀个线程sleep时多次访问互斥资源,所以sleep要在得到互斥锁后调⽤*/    sleep (1);
/*临界资源*/
gnum++;
printf ("Thread_2 add one to num:%d/n",gnum);
pthread_mutex_unlock(&mutex); /*释放互斥锁*/
}
pthread_exit ( NULL );
}
有两类函数在这⾥说明⼀下:
第⼀类是互斥锁:
linux下为了多线程同步,通常⽤到锁的概念。
posix下抽象了⼀个锁类型的结构:ptread_mutex_t。通过对该结构的操作,来判断资源是否可以访问。顾名思义,加锁(lock)后,别⼈就⽆法打开,只有当锁没有关闭(unlock)的时候才能访问资源。
即对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于⼀个可称为” 互斥锁” 的标记,这个标记⽤来保证在任⼀时刻,只能有⼀个线程访问该对象。
使⽤互斥锁(互斥)可以使线程按顺序执⾏。通常,互斥锁通过确保⼀次只有⼀个线程执⾏代码的临界段来同步多个线程。互斥锁还可以保护单线程代码。
  要更改缺省的互斥锁属性,可以对属性对象进⾏声明和初始化。通常,互斥锁属性会设置在应⽤程序开头的某个位置,以便可以快速查和轻松修改。
1.函数原型:
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
该函数⽤于C函数的多线程编程中,互斥锁的初始化。
  pthread_mutex_init()函数是以动态⽅式创建互斥锁的,参数attr指定了新建互斥锁的属性。如果参数attr为NULL,则使⽤默认的互斥锁属性,默认属性为快速互斥锁 。互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有⼀个锁类型属性,不同的锁类型在试图对⼀个已经被锁定的互斥锁加锁时表现不同。
  pthread_mutexattr_init()函数成功完成之后会返回零,其他任何返回值都表⽰出现了错误。
  函数成功执⾏后,互斥锁被初始化为锁住态。
2. 互斥锁属性
互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有⼀个锁类型属性,不同的锁类型在试图对⼀个已经被锁定的互斥锁加锁时表现不同。当前(glibc2.2.3,linuxthreads0.9)有四个值可供选择:
  * PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当⼀个线程加锁以后,其余请求锁的线程将形成⼀个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。
  * PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同⼀个线程对同⼀个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
  * PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同⼀个线程请求同⼀个锁,则返回EDEADLK,否则与
PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。
  * PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。
3. 其他锁操作
  锁操作主要包括加锁pthread_mutex_lock()、解锁pthread_mutex_unlock()和测试加锁 pthread_mutex_trylock()三个,不论哪种类型的锁,都不可能被两个不同的线程同时得到,⽽必须等待解锁。对于普通锁和适应锁类型,解锁者可以是同进程内任何线程;⽽检错锁则必须由加锁者解锁才有效,否则返回EPERM;对于嵌套锁,⽂档和实现要求必须由加锁者解锁,但实验结果表明并没有这种限制,这个不同⽬前还没有得到解释。在同⼀进程中的线程,如果加锁后没有解锁,则任何其他线程都⽆法再获得锁。
int pthread_mutex_lock(pthread_mutex_t *mutex)
  int pthread_mutex_unlock(pthread_mutex_t *mutex)
  int pthread_mutex_trylock(pthread_mutex_t *mutex)
  pthread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY⽽不是挂起等待。
1. 死锁:
  死锁主要发⽣在有多个依赖锁存在时, 会在⼀个线程试图以与另⼀个线程相反顺序锁住互斥量时发⽣. 如何避免死锁是使⽤互斥量应该格外注意的东西。
  总体来讲, 有⼏个不成⽂的基本原则:
  对共享资源操作前⼀定要获得锁。
  完成操作以后⼀定要释放锁。
  尽量短时间地占⽤锁。
  如果有多锁, 如获得顺序是ABC连环扣, 释放顺序也应该是ABC。
  线程错误返回时应该释放它所获得的锁。
第⼆类是多线程:
pthread_create函数
原型:int  pthread_create((pthread_t  *thread,
pthread_attr_t  *attr,
void  *(*start_routine)(void  *),
void  *arg)
⽤法:#include  <pthread.h>
功能:创建线程(实际上就是确定调⽤该线程函数的⼊⼝点),在线程创建以后,就开始运⾏相关的线程函数。
说明:thread:线程标识符;
attr:线程属性设置;
start_routine:线程函数的起始地址;
arg:传递给start_routine的参数;
返回值:成功,返回0;出错,返回-1。
pthread_join函数
函数原型:int pthread_join(pthread_t tid, void **status);
功能:pthread_join()函数会⼀直阻塞调⽤线程,直到指定的线程tid终⽌。当pthread_join()返回之后,应⽤程序可回收
与已终⽌线程关联的任何数据存储空间,(另外也可设置线程attr属性,当线程结束时直接回收资源)如果没有必要等待特定的线程终⽌之后才进⾏其他处理,则应当将该线程分离pthread_detach()。
头⽂件:#include <pthread.h>
pthread⾮linux系统的默认库,需⼿动链接-线程库 -lpthread
参数:
tid:需要等待的线程,指定的线程必须位于当前的进程中,⽽且不得是分离线程
status:线程tid所执⾏的函数start_routine的返回值(start_routine返回值地址需要保证有效),其中status可以为nullptr
返回值:
调⽤成功完成后,pthrea_join()返回0.其他任何返回值都表⽰出现了错误。如果检测到以下任意情况
pthread_join()将失败并返回相应的值
ESRCH
描述: 没有到与给定的线程ID 相对应的线程。(如果多个线程等待同⼀个线程终⽌,则所有等待线程将⼀直等到⽬标线程终⽌。
然后⼀个等待线程成功返回。其余的等待线程将失败返回ESRCH错误)
EDEADLK
描述: 将出现死锁,如⼀个线程等待其本⾝,或者线程A和线程B 互相等待。
EINVAL
描述: 与给定的线程ID 相对应的线程是分离线程。
pthread_exit函数

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。