文章先分享一篇之前讲线程的博客,建议先阅读此博客:
基本线程函数
pthread_create:
int pthread_create(pthread_t *tid, const pthread_attr_t *attr,void *(*func)(void *),void *arg);
//成功返回0
tid为线程ID,线程有许多属性:优先级,初始栈大小....pthread_attr_t变量指向这些属性。
func指向要执行的线程函数,arg为函数调用参数,可以是一个结构体(包含多个参数)。
pthread_join函数
调用pthread_join函数等待一个给定线程终止。
int pthread_join(pthread_t *tid, void **status);
//成功返回0
tid指定要等待的线程。返回值保存到status中。
pthread_self函数
此函数获取自身的线程ID
pthread_t pthread_self(void);
//返回调用线程的线程ID
pthread_detach函数
一个线程或者是可汇合的(joinable,默认值),或者是脱离的(detached)。当一个可汇合的线程终止时,它的线程ID和退出状态将留存到另一个线程对它调用pthread_join。脱离的线程却像守护进程,当它们终止时,所有相关资源被释放,我们不能等待它们终止。如果一个线程需要直到另一个线程什么时候终止,那就最好保持第二个线程的可汇合状态。
pthread_detach函数把指定的线程转变成脱离状态。
int pthread_detach(pthread_t tid);
//成功返回0
pthread_exit函数
让一个线程终止方法1:
void pthread_exit(void *status);
让一个线程终止的另外两种方法:
1.启动线程的函数可以返回。
2.如果进程的main函数返回或者任何线程调用了exit,整个进程就终止,其中包含它的任何线程。
使用线程的str_cli函数
#include "unpthread.h"
void copyto(void *);
static int sockfd; //global for both thread to access
static FILE *fp;
void str_cli(FILE *fp_arg, int sockfd_arg)
{
char recvline[MAXLINE];
pthread_t tid;
sockfd = sockfd_arg; //copy arguments to externals
fp = fp_arg;
pthread_create(&tid, NULL, copyto, NULL);
/* 主线程循环:把从套接字读入的每个文本行父复制到标准输出 */
while(readline(sockfd, recvline, MAXLINE) > 0)
fputs(recvline,stdout);
}
/* copyto线程把读自标准输入的每个文本行复制到套接字。当读取到EOF时,通过调用shutdown发送FIN */
void *copyto(void *arg)
{
char sendline[MAXLINE];
while(fgets(sendline, MAXLINE, fp) != NULL)
writen(sockfd,sendline,strlen(sendline));
shutdown(sockfd,SHUT_WR); //EOF on stdin, send FIN
return(NULL);
}
使用线程的TCP回射服务器程序
#include "unpthread.h"
static void *doit(void *); //each thread executes this function
int main(int argc, char **argv)
{
int listenfd, connfd;
pthread_t tid;
socklen_t addrlen, len;
struct sockaddr *cliaddr;
if (argc == 2)
listenfd = tcp_listen(NULL,argv[1],&addrlen);
else if (argc == 3)
listenfd = tcp_listen(argv[1],argv[2],&addrlen);
else
err_quit("usage: tcpserv01 [ <host> ] <service or port>");
cliaddr = malloc(addrlen);
while (1) {
len = addrlen;
connfd = accept(listenfd, cliaddr, &len);
pthread_create(&tid, NULL, &doit, (void *) connfd);
}
}
static void* doit(void *arg)
{
pthread_detach(pthread_self());
str_echo((int) arg); //same function as before
close( (int) arg); //done with connected socket
return(NULL);
}
互斥锁
我们称线程编程为并发编程或并行编程,因为多个线程可以并发地运行且访问相同的变量。
先看一个示例:两个线程不正确地递增一个全局变量
创建两个线程,然后让每个线程递增同一个全局变量10次。
#include "unp.h"
#include<pthread.h>
#define NLOOP 10
int counter;
void *doit(void *);
int main(int argc, char **argv)
{
pthread_t tidA, tidB;
pthread_create(&tidA,NULL,&doit,NULL);
pthread_create(&tidB,NULL,&doit,NULL);
//wait for both threads to terminate
pthread_join(tidA,NULL);
pthread_join(tidB,NULL);
exit(0);
}
void *doit(void *vptr)
{
int i, val;
for (i = 0; i < NLOOP; i++) {
val = counter;
printf("%d: %d\n",pthread_self(), val + 1);
counter = val + 1;
}
return NULL;
编译:gcc -o pthread_wrong pthread_wrong.c -lpthread
运行结果:
多个线程更改一个共享变量的问题,解决方法时使用一个互斥锁(mutex)保护这个共享变量。访问该变量的前提是持有该互斥锁。
互斥锁类型是pthread_mutex_t的变量,使用以下两个函数为一个互斥锁上锁和解锁。
#include<pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mptr);
int pthread_mutex_unlock(pthread_mutex_t *mptr); //若成功返回0
如果试图上锁已被某个线程锁住的一个互斥锁,本线程将被阻塞,直到该互斥锁被解锁。
如果某个互斥锁变量是静态分配的,必须把它初始化为常值PTHREAD_MUTEX_INITIALIZER
更改刚才的版本,使用单个互斥锁保护由两个线程共同访问的计数器。
#include "unp.h"
#include<pthread.h>
#define NLOOP 10
int counter;
pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;
void *doit(void *);
int main(int argc, char **argv)
{
pthread_t tidA, tidB;
pthread_create(&tidA,NULL,&doit,NULL);
pthread_create(&tidB,NULL,&doit,NULL);
//wait for both threads to terminate
pthread_join(tidA,NULL);
pthread_join(tidB,NULL);
exit(0);
}
void *doit(void *vptr)
{
int i, val;
for (i = 0; i < NLOOP; i++) {
pthread_mutex_lock(&counter_mutex);
val = counter;
printf("%d: %d\n",pthread_self(), val + 1);
counter = val + 1;
pthread_mutex_unlock(&counter_mutex);
}
return NULL;
}
编译:
gcc -o pthread_mutex pthread_mutex.c -lpthread
运行结果:
条件变量
互斥锁适合于防止同时访问某个共享变量,但我们需要另外某种在等待某个条件发生期间能让我们进入睡眠的东西。
需要一个让主循环进入睡眠,直到某个线程通知它有事可做才醒来的方法。条件变量结合互斥锁能够提供这个功能。互斥锁提供互斥机制,条件变量提供信号机制。
条件变量的类型为pthread_cond_t。以下两个函数使用条件变量
#include<pthread.h>
int pthread_cond_wait(pthread_cond_t *cptr, pthread_mutex_t *mptr);
int pthread_cond_signal(pthread_cond_t *cptr);