使用线程的几种方式(三)客户/服务器

在客户端/服务器系统中,客户请求服务器对一组数据执行某个操作,服务器独立的执行。以下示例程序从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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值