讲Linux下面线程的操作、多线程的同步和互斥相关函数及操作的文章链接

41 篇文章 0 订阅
4 篇文章 0 订阅

Linux多线程编程(不限Linux)

http://www.cnblogs.com/skynet/archive/2010/10/30/1865267.html

3、有关线程操作的函数

#include <pthread.h>
还需包含
 
int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*func) (void *), void *arg);
int pthread_join (pthread_t tid, void ** status);
pthread_t pthread_self (void);
int pthread_detach (pthread_t tid);
void pthread_exit (void *status);

pthread_create用于创建一个线程,成功返回0,否则返回Exxx(为正数)。

  • pthread_t *tid:线程id的类型为pthread_t,通常为无符号整型,当调用pthread_create成功时,通过*tid指针返回。
  • const pthread_attr_t *attr:指定创建线程的属性,如线程优先级、初始栈大小、是否为守护进程等。可以使用NULL来使用默认值,通常情况下我们都是使用默认值。
  • void *(*func) (void *):函数指针func,指定当新的线程创建之后,将执行的函数。
  • void *arg:线程将执行的函数的参数。如果想传递多个参数,请将它们封装在一个结构体中。

pthread_join用于等待某个线程退出,成功返回0,否则返回Exxx(为正数)。

  • pthread_t tid:指定要等待的线程ID
  • void ** status:如果不为NULL,那么线程的返回值存储在status指向的空间中(这就是为什么status是二级指针的原因!这种才参数也称为“值-结果”参数)。

pthread_self用于返回当前线程的ID。

pthread_detach用于是指定线程变为分离状态,就像进程脱离终端而变为后台进程类似。成功返回0,否则返回Exxx(为正数)。变为分离状态的线程,如果线程退出,它的所有资源将全部释放。而如果不是分离状态,线程必须保留它的线程ID,退出状态直到其它线程对它调用了pthread_join

进程也是类似,这也是当我们打开进程管理器的时候,发现有很多僵死进程的原因!也是为什么一定要有僵死这个进程状态。

pthread_exit用于终止线程,可以指定返回值,以便其他线程通过pthread_join函数获取该线程的返回值。

  • void *status:指针线程终止的返回值。

互斥锁的相关操作函数如下:

#include <pthread.h> 

int pthread_mutex_lock(pthread_mutex_t * mptr); 
int pthread_mutex_unlock(pthread_mutex_t * mptr); 
//Both return: 0 if OK, positive Exxx value on error

在对临界资源进行操作之前需要pthread_mutex_lock先加锁,操作完之后pthread_mutex_unlock再解锁。而且在这之前需要声明一个pthread_mutex_t类型的变量,用作前面两个函数的参数。

条件变量的相关函数如下:

#include <pthread.h>
 
int pthread_cond_wait(pthread_cond_t *cptr, pthread_mutex_t *mptr); 
int pthread_cond_signal(pthread_cond_t *cptr); 
//Both return: 0 if OK, positive Exxx value on error

pthread_cond_wait用于等待某个特定的条件为真,pthread_cond_signal用于通知阻塞的线程某个特定的条件为真了。在调用者两个函数之前需要声明一个pthread_cond_t类型的变量,用于这两个函数的参数。

为什么条件变量始终与互斥锁一起使用,对条件的测试是在互斥锁(互斥)的保护下进行的呢?因为“某个特性条件”通常是在多个线程之间共享的某个变量。互斥锁允许这个变量可以在不同的线程中设置和检测。

通常,pthread_cond_wait只是唤醒等待某个条件变量的一个线程。如果需要唤醒所有等待某个条件变量的线程,需要调用:

int pthread_cond_broadcast (pthread_cond_t * cptr);

默认情况下面,阻塞的线程会一直等待,知道某个条件变量为真。如果想设置最大的阻塞时间可以调用:

int pthread_cond_timedwait (pthread_cond_t * cptr, pthread_mutex_t *mptr, const struct timespec *abstime);

如果时间到了,条件变量还没有为真,仍然返回,返回值为ETIME。

/************pthread_cond_wait()的使用方法**********/
    pthread_mutex_lock(&qlock);    
    pthread_cond_wait(&qready, &qlock);
    pthread_mutex_unlock(&qlock);
/*****************************************************/
 
The mutex passed to pthread_cond_wait protects the condition.The caller passes it locked to the function, which then atomically places them calling thread on the list of threads waiting for the condition and unlocks the mutex. This closes the window between the time that the condition is checked and the time that the thread goes to sleep waiting for the condition to change, so that the thread doesn't miss a change in the condition. When pthread_cond_wait returns, the mutex is again locked.
上面是APUE的原话,就是说pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)函数传入的参数mutex用于保护条件,因为我们在调用pthread_cond_wait时,如果条件不成立我们就进入阻塞,但是进入阻塞这个期间,如果条件变量改变了的话,那我们就漏掉了这个条件。因为这个线程还没有放到等待队列上,所以调用pthread_cond_wait前要先锁互斥量,即调用pthread_mutex_lock(),pthread_cond_wait在把线程放进阻塞队列后,自动对mutex进行解锁,使得其它线程可以获得加锁的权利。这样其它线程才能对临界资源进行访问并在适当的时候唤醒这个阻塞的进程。当pthread_cond_wait返回的时候又自动给mutex加锁。
实际上边代码的加解锁过程如下:
/************pthread_cond_wait()的使用方法**********/
pthread_mutex_lock(&qlock);    /*lock*/
pthread_cond_wait(&qready, &qlock); /*block-->unlock-->wait() return-->lock*/
pthread_mutex_unlock(&qlock); /*unlock*/
/*****************************************************/

#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>

void* testThreadPool(int *t);
pthread_mutex_t clifd_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t clifd_cond = PTHREAD_COND_INITIALIZER;
int a = 0;

int main() {

int sock_fd, conn_fd;
int optval;

socklen_t cli_len;
struct sockaddr_in cli_addr, serv_addr;


sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (sock_fd < 0) {
   printf("socket\n");
}

optval = 1;
if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (void *) &optval,
    sizeof(int)) < 0) {
   printf("setsockopt\n");
}

memset(&serv_addr, 0, sizeof(struct sockaddr_in));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(4507);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

if (bind(sock_fd, (struct sockaddr *) &serv_addr,
    sizeof(struct sockaddr_in)) < 0) {
   printf("bind\n");
}

if (listen(sock_fd, 100) < 0) {
   printf("listen\n");
}

cli_len = sizeof(struct sockaddr_in);
int t;
pthread_t * mythread;
mythread = (pthread_t*) malloc(100 * sizeof(pthread_t));
for (t = 0; t < 5; t++) {
   int *i=(int*)malloc(sizeof(int));
   *i=t;
   if (pthread_create(&mythread[t], NULL, (void*)testThreadPool, (void*)i) != 0) {
    printf("pthread_create");
   }
}

while (1) {
   conn_fd = accept(sock_fd, (struct sockaddr *) &cli_addr, &cli_len);
   if (conn_fd < 0) {
    printf("accept\n");
   }
   printf("accept a new client, ip:%s\n", inet_ntoa(cli_addr.sin_addr));

   pthread_mutex_lock(&clifd_mutex);
   a=conn_fd;
   pthread_cond_signal(&clifd_cond);
   pthread_mutex_unlock(&clifd_mutex);
}
return 0;
}

void* testThreadPool(int *t) {

printf("t is %d\n", *t);
for (;;) {
   pthread_mutex_lock(&clifd_mutex);
   pthread_cond_wait(&clifd_cond, &clifd_mutex);
   printf("a is %d\n", a);
   printf("t is %d\n", *t);
   pthread_mutex_unlock(&clifd_mutex);
   sleep(100);
}
return (void*) 0;
}

了解 pthread_cond_wait() 的作用非常重要 -- 它是 POSIX 线程信号发送系统的核心,也是最难以理解的部分。 

首先,让我们考虑以下情况:线程为查看已链接列表而锁定了互斥对象,然而该列表恰巧是空的。这一特定线程什么也干不了 -- 其设计意图是从列表中除去节点,但是现在却没有节点。因此,它只能: 

锁定互斥对象时,线程将调用 pthread_cond_wait(&mycond,&mymutex)。pthread_cond_wait() 调用相当复杂,因此我们每次只执行它的一个操作。 

pthread_cond_wait() 所做的第一件事就是同时对互斥对象解锁(于是其它线程可以修改已链接列表),并等待条件 mycond 发生(这样当 pthread_cond_wait() 接收到另一个线程的“信号”时,它将苏醒)。现在互斥对象已被解锁,其它线程可以访问和修改已链接列表,可能还会添加项。 【要求解锁并阻塞是一个原子操作

此时,pthread_cond_wait() 调用还未返回。对互斥对象解锁会立即发生,但等待条件 mycond 通常是一个阻塞操作,这意味着线程将睡眠,在它苏醒之前不会消耗 CPU 周期。这正是我们期待发生的情况。线程将一直睡眠,直到特定条件发生,在这期间不会发生任何浪费 CPU 时间的繁忙查询。从线程的角度来看,它只是在等待 pthread_cond_wait() 调用返回。 

现在继续说明,假设另一个线程(称作“2 号线程”)锁定了 mymutex 并对已链接列表添加了一项。在对互斥对象解锁之后,2 号线程会立即调用函数 pthread_cond_broadcast(&mycond)。此操作之后,2 号线程将使所有等待 mycond 条件变量的线程立即苏醒。这意味着第一个线程(仍处于 pthread_cond_wait() 调用中)现在将苏醒。 

现在,看一下第一个线程发生了什么。您可能会认为在 2 号线程调用 pthread_cond_broadcast(&mymutex) 之后,1 号线程的 pthread_cond_wait() 会立即返回。不是那样!实际上,pthread_cond_wait() 将执行最后一个操作:重新锁定 mymutex。一旦 pthread_cond_wait() 锁定了互斥对象,那么它将返回并允许 1 号线程继续执行。那时,它可以马上检查列表,查看它所感兴趣的更改。 

停止并回顾! 
那个过程非常复杂,因此让我们先来回顾一下。第一个线程首先调用: 
pthread_mutex_lock(&mymutex); 
然后,它检查了列表。没有找到感兴趣的东西,于是它调用:
pthread_cond_wait(&mycond, &mymutex); 
 
然后,pthread_cond_wait() 调用在返回前执行许多操作: 
 
pthread_mutex_unlock(&mymutex); 
 
它对 mymutex 解锁,然后进入睡眠状态,等待 mycond 以接收 POSIX 线程“信号”。一旦接收到“信号”(加引号是因为我们并不是在讨论传统的 UNIX 信号,而是来自 pthread_cond_signal() 或 pthread_cond_broadcast() 调用的信号),它就会苏醒。但 pthread_cond_wait() 没有立即返回 -- 它还要做一件事:重新锁定 mutex:
pthread_mutex_lock(&mymutex); 
 
pthread_cond_wait() 知道我们在查找 mymutex “背后”的变化,因此它继续操作,为我们锁定互斥对象,然后才返回。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值