在客户端/服务器系统中,客户请求服务器对一组数据执行某个操作,服务器独立的执行。以下示例程序从stdin读入数据,通过获取所mutex,实现有序。
/*
* server.c
* 线程使用方式(3),客户端-服务器 示例
* gcc -g -o server -lpthread -Wall server.c
*/
#include <pthread.h>
#include <math.h>
#include "errors.h"
#define CLIENT_THREADS 4 /*客户端线程数量*/
#define REQ_READ 1
#define REQ_WRITE 2
#define REQ_QUIT 3
typedef struct request_tag {
struct request_tag *next;
int operation;
int synchronous;
int done_flag;
pthread_cond_t done;
char prompt[32];
char text[128];
} request_t;
/* 互斥锁mutex每个request_t都需要竞争*/
typedef struct tty_server_tag {
request_t *first;
request_t *last;
int running;
pthread_mutex_t mutex;
pthread_cond_t request;
} tty_server_t;
/*服务器环境*/
tty_server_t tty_server = {NULL, NULL, 0, PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER};
int client_threads;
/* 提供了等待创建的所有线程都结束的一种方式.
* 当当前线程数量大于0时,等待条件变量done,在每个线程结束时,count减一,
* 并发送信号
*/
pthread_mutex_t client_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t client_cond = PTHREAD_COND_INITIALIZER;
/*
* 服务器线程函数,循环处理请求,直到被要求退出
*/
void *tty_server_routine(void *args)
{
//static pthread_mutex_t prompt_mutex = PTHREAD_MUTEX_INITIALIZER;
request_t *request;
int operation, len;
int status;
while(1) {
/*等待、获取请求数据*/
status = pthread_mutex_lock(&tty_server.mutex);
if(status != 0) err_abort(status, "lock tty_server failed");
while(tty_server.first == NULL) {
status = pthread_cond_wait(&tty_server.request, &tty_server.mutex);
if(status != 0) err_abort(status, "wait cond request failed");
}
request = tty_server.first;
tty_server.first = tty_server.first->next;
if(tty_server.first == NULL) tty_server.last = NULL;
status = pthread_mutex_unlock(&tty_server.mutex);
if(status != 0) err_abort(status, "unlock tty_server failed");
/*处理数据*/
operation = request->operation;
switch(operation) {
case REQ_QUIT:
break;
case REQ_READ:
if(strlen(request->prompt) > 0) printf(request->prompt);
if(fgets(request->text, 128, stdin) == NULL) request->text[0] = '\0';
len = strlen(request->text);
if(len > 0 && request->text[len - 1] == '\n') request->text[len - 1] ='\0';
break;
case REQ_WRITE:
puts(request->text);
break;
default:
break;
}
if(request->synchronous) {
status = pthread_mutex_lock(&tty_server.mutex);
if(status != 0) err_abort(status, "lock tty_server failed");
request->done_flag = 1;
status = pthread_cond_signal(&request->done);
if(status != 0) err_abort(status, "signal cond request failed");
status = pthread_mutex_unlock(&tty_server.mutex);
if(status != 0) err_abort(status, "unlock tty_server failed");
} else
free(request);
if(operation == REQ_QUIT) break;
}
return NULL;
}
void tty_server_request(int operation, int sync, const char *prompt, char *string)
{
request_t *request;
int status;
status = pthread_mutex_lock(&tty_server.mutex);
if(status != 0) err_abort(status, "lock tty_server failed");
/* 在任何一个时间点上,线程是可结合的(joinable),或者是分离的(detached)。
* 一个可结合的线程能够被其他线程收回其资源和杀死;在被其他线程回收之前,
* 它的存储器资源(如栈)是不释放的。相反,一个分离的线程是不能被其他线程
* 回收或杀死的,它的存储器资源在它终止时由系统自动释放。
* 线程的分离状态决定一个线程以什么样的方式来终止自己。在上面的例子中,
* 我们采用了线程的默认属性,即为非分离状态(即可结合的,joinable,需要回收),
* 这种情况下,原有的线程等待创建的线程结束;只有当pthread_join()函数返回时,
* 创建的线程才算终止,才能释放自己占用的系统资源。而分离线程不是这样子的,
* 它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。
* 程序员应该根据自己的需要,选择适当的分离状态。
* 设置线程分离状态的函数为pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。
* 第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)。
* 这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在pthread_create
* 函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,
* 这样调用pthread_create的线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,
* 最简单的方法之一是可以在被创建的线程里调用pthread_cond_timewait函数,让这个线程等待一会儿,
* 留出足够的时间让函数pthread_create返回。设置一段等待时间,是在多线程编程里常用的方法。
* 但是注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。
* 另外一个可能常用的属性是线程的优先级,它存放在结构sched_param中。用函数pthread_attr_getschedparam
* 和函数pthread_attr_setschedparam进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。
*
* 如果不关心一个线程的结束状态,那么也可以将一个线程设置为 detached 状态,
* 从而让操作系统在该线程结束时来回收它所占的资源。将一个线程设置为detached
* 状态可以通过两种方式来实现。一种是调用 pthread_detach() 函数,可以将线程 th
* 设置为 detached 状态。另一种方法是在创建线程时就将它设置为 detached 状态,
* 首先初始化一个线程属性变量,然后将其设置为 detached 状态,最后将它作为参数传入
* 线程创建函数 pthread_create(),这样所创建出来的线程就直接处于 detached 状态。
*/
if(!tty_server.running) {
pthread_t thread;
pthread_attr_t detached_attr;
pthread_attr_init(&detached_attr);
pthread_attr_setdetachstate(&detached_attr, PTHREAD_CREATE_DETACHED);
tty_server.running = 1;
pthread_create(&thread, &detached_attr, tty_server_routine, NULL);
pthread_attr_destroy(&detached_attr);
}
request = (request_t*)malloc(sizeof(request_t));
if(request == NULL) errno_abort("allocate failed");
request->next = NULL;
request->operation = operation;
request->synchronous = sync;
if(sync) {
request->done_flag = 0;
pthread_cond_init(&request->done, NULL);
}
if(prompt != NULL) strncpy(request->prompt, prompt, 32);
else request->prompt[0] = '\0';
if(operation == REQ_WRITE && string != NULL) strncpy(request->text, string, 128);
else request->text[0] = '\0';
if(tty_server.first == NULL) {
tty_server.first = request;
tty_server.last = request;
} else {
(tty_server.last)->next = request;
tty_server.last = request;
}
pthread_cond_signal(&tty_server.request);
if(sync) {
while(!request->done_flag) {
pthread_cond_wait(&request->done, &tty_server.mutex);
}
if(operation == REQ_READ) {
if(strlen(request->text) > 0) strcpy(string, request->text);
else string[0] = '\0';
}
pthread_cond_destroy(&request->done);
free(request);
}
status = pthread_mutex_unlock(&tty_server.mutex);
if(status != 0) err_abort(status, "unlock tty_server failed");
}
void *client_routine(void *arg)
{
int my_number = *(int*)arg;
int loops;
char prompt[32];
char string[128], formatted[128];
int status;
sprintf(prompt, "Client %d> ", my_number);
while(1) {
tty_server_request(REQ_READ, 1, prompt, string);
if(strlen(string) == 0) break;
for(loops = 0; loops < 4; loops++) {
sprintf(formatted, "(%d#%d) %s", my_number, loops, string);
tty_server_request(REQ_WRITE, 0, NULL, string);
sleep(1);
}
}
status = pthread_mutex_lock(&client_mutex);
if(status != 0) err_abort(status, "lock tty_server failed");
client_threads--;
if(client_threads <= 0)
pthread_cond_signal(&client_cond);
status = pthread_mutex_unlock(&client_mutex);
if(status != 0) err_abort(status, "unlock tty_server failed");
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t thread;
int count;
int status;
client_threads = CLIENT_THREADS;
for(count = 0; count < client_threads; count++) {
pthread_create(&thread, NULL, client_routine, (void*)(&count));
}
status = pthread_mutex_lock(&client_mutex);
if(status != 0) err_abort(status, "lock tty_server failed");
while(client_threads > 0) {
pthread_cond_wait(&client_cond, &client_mutex);
}
status = pthread_mutex_unlock(&client_mutex);
if(status != 0) err_abort(status, "unlock tty_server failed");
printf("All client done!\n");
tty_server_request(REQ_QUIT, 1, NULL, NULL);
return 0;
}