Linux多线程之同步3
需求
客户端将需要解决的task发送给服务器,服务器调用线程来解决客户端发送的task,解决完由线程负责将其发送回客户端。(用管道实现通信)
思路
1. server维护两个列表。一是客户端列表。二是任务列表。分别如下:
/* 客户端列表 */
typedef struct tag_fds
{
int s_rfd ;
int s_wfd ;
struct tag_fds* s_next ;
}FD_PAIR, *pFD_PAIR; /* 任务列表,相当于资源 */
typedef struct tag_que
{
TASK s_arr[TASK_CNT + 1] ;
int s_front ;
int s_tail ;
}QUEUE, *pQUEUE ;
2. server端维护一个管道(为了叙述方便,暂时称为server_pipe),用于接收客户端的上线消息。client端维护两个管道,一个管道用于向server端发送所要处理的task,而另一个管道用于接收从server端返回的task result。
3. server端可以使用select函数对所有管道的读端进行轮询。所有的读端包括:用于接收客户端task管道的读端以及server_pipe的读端。
4. 当客户端上线时,它会将自己的进程ID(pid)通过server_pipe发送给服务器。服务器根据pid,可以构造出该客户端所创建的两个管道的名称,以此可以打开客户端的两个管道,同时将server端的读端加入监听集合,并且将该客户端加入客户端队列。
5. 当客户端向服务器发送task时,服务器端的select监听到之后,会遍历客户端列表(根据server端接收task管道的读描述符),找到具体的客户端。我们会将服务器对应于该客户端管道的写描述符连同task任务一起打包(其实就是结构体啦),加入任务列表。之所以要将写描述符打包进一个任务,是因为方便我们的线程处理完任务后,可以直接向客户端返回结果。任务结构体如下:
typedef struct tag_task
{
char s_msg[1024]; /* 客户端向服务器端发送的任务用msg存储 */
int s_fd ; /* s_fd为写端,用于线程处理完任务后,发送消息给客户端 */
}TASK, *pTASK;
6. 很显然,我们服务器端的主线程在此处就是一个生产者,负责将TASK添加到任务列表中。而主线程创造出的诸多线程则是消费者,从任务列表取出任务,处理完后发送结果至客户端。
7. 此处服务器处理逻辑比较简单,客户端发送什么请求,我们就返回什么请求,打印在屏幕上。
代码
头文件server.h
#ifndef __SERVER_H__
#define __SERVER_H__
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/time.h>
#include <sys/select.h>
#include <signal.h>
#define MSG_LEN 1024
#define TASK_CNT 1024
extern pthread_mutex_t mutex ;
extern pthread_cond_t cond_master ;
extern pthread_cond_t cond_slave ; typedef struct tag_fds
{
int s_rfd ;
int s_wfd ;
struct tag_fds* s_next ;
}FD_PAIR, *pFD_PAIR; typedef struct tag_task
{
char s_msg[1024];
int s_fd ;
}TASK, *pTASK; typedef struct tag_que
{
TASK s_arr[TASK_CNT + 1] ;
int s_front ;
int s_tail ;
}QUEUE, *pQUEUE ; void fds_link_init(pFD_PAIR* phead);
void fds_insert(pFD_PAIR* phead, int fd_r, int fd_w);
int fds_find_wfd(pFD_PAIR phead, int fd_r);
void fds_link_delete(pFD_PAIR* phead,int fd_r); void add_task(pQUEUE pq,pTASK pt );
void get_task(pQUEUE pq, pTASK pt);
void excute_task(pTASK pt); #endif
服务器主线程main.c
/*************************************************************************
> File Name: ./src/main.c
> Author: KrisChou
> Mail:zhoujx0219@163.com
> Created Time: Tue 26 Aug 2014 07:55:50 PM CST
************************************************************************/
#include "server.h"
pthread_mutex_t mutex ;
pthread_cond_t cond_master ;
pthread_cond_t cond_slave ;
void* slave_handler(void* arg)
{
pthread_detach(pthread_self());
pQUEUE pq = (pQUEUE)arg ;
TASK my_task ;
while(1)
{
get_task(pq, &my_task);
excute_task(&my_task);
sleep(1);
}
}
int main(int argc, char* argv[])// exe fifo_name thd_cnt
{
if(argc != 3)
{
printf("USAGE: EXE FILENAME THD_CNT ! \n");
exit(1);
}
signal(SIGINT, SIG_IGN);
signal(SIGPIPE,SIG_IGN);
signal(SIGQUIT,SIG_IGN); int fd_server ;
QUEUE my_que ;
pFD_PAIR my_list ; fd_set read_set, ready_set ;
struct timeval tm ;
int select_ret ; memset(&my_que, 0, sizeof(QUEUE));
fds_link_init(&my_list); int slave_cnt = atoi(argv[2]);
pthread_t * arr = (pthread_t*)calloc(slave_cnt, sizeof(pthread_t)); pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond_master, NULL);
pthread_cond_init(&cond_slave, NULL); int index = 0 ;
while(slave_cnt > 0)
{
pthread_create(arr + index, NULL,slave_handler, (void*)&my_que );
slave_cnt -- ;
index ++ ;
} fd_server = open(argv[1], O_RDONLY);
if(fd_server == -1)
{
perror("open");
exit(-1);
}
FD_ZERO(&read_set);
FD_ZERO(&ready_set);
FD_SET(fd_server, &read_set);
while(1)
{
tm.tv_sec = 0 ;
tm.tv_usec = 1000 ;
ready_set = read_set ;
select_ret = select(1024,&ready_set, NULL, NULL, &tm ); if(select_ret == 0)
{
continue ;
}else if(select_ret > 0)
{
if(FD_ISSET(fd_server, &ready_set))// client on r.pid w.pid
{
char buf[32];
memset(buf, 0, 32);
if(read(fd_server, buf, 32) == 0)
{
continue ;
}else
{
printf("a client on ! \n");
char pipe_name[32];
memset(pipe_name, 0, 32);
buf[strlen(buf) - 1] = '\0';
sprintf(pipe_name,"r.%s",buf);//clinet read
int wfd, rfd ;
wfd = open(pipe_name, O_WRONLY);
memset(pipe_name, 0, 32);
sprintf(pipe_name,"w.%s",buf);//clinet write
rfd = open(pipe_name, O_RDONLY);
fds_insert(&my_list, rfd, wfd);
FD_SET(rfd, &read_set);
}
}
pFD_PAIR pCur = my_list ;
while(pCur)
{
if(FD_ISSET(pCur ->s_rfd, &ready_set))// client request
{
char buf[1024] ;
memset(buf, 0, 1024);
if(read(pCur ->s_rfd, buf, 1024) == 0)//client quit
{
FD_CLR(pCur ->s_rfd, &read_set);
int fd_r = pCur ->s_rfd ;
pCur = pCur -> s_next ;
fds_link_delete(&my_list, fd_r); }else
{
TASK tk ;
memset(&tk, 0, sizeof(tk));
tk.s_fd = pCur -> s_wfd ;
strcpy(tk.s_msg, buf);
add_task(&my_que, &tk);
pCur = pCur ->s_next ;
}
}else
{
pCur = pCur ->s_next ;
}
}
} } pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond_master);
pthread_cond_destroy(&cond_slave);
return 0 ;
}
fds_link.c
/*************************************************************************
> File Name: ../src/fds_link.c
> Author: KrisChou
> Mail:zhoujx0219@163.com
> Created Time: Tue 26 Aug 2014 05:40:08 PM CST
************************************************************************/
#include "server.h" void fds_link_init(pFD_PAIR* phead)
{
*phead = NULL ;
} void fds_insert(pFD_PAIR* phead, int fd_r, int fd_w)
{
pFD_PAIR pCur = (pFD_PAIR)calloc(1, sizeof(FD_PAIR));
pCur ->s_rfd = fd_r ;
pCur ->s_wfd = fd_w ;
pCur ->s_next = *phead ;
*phead = pCur ; }
/*
int fds_find_wfd(pFD_PAIR phead, int fd_r)
{
while(phead)
{
if(phead ->s_rfd == fd_r)
{
break ;
}else
{
phead = phead ->s_next ;
}
}
if(phead == NULL)
{
return -1 ;
}else
{
return phead ->s_wfd ;
}
} */ void fds_link_delete(pFD_PAIR* phead,int fd_r)
{
pFD_PAIR pPre , pCur ;
pPre = NULL ;
pCur = *phead ;
while(pCur)
{
if(pCur ->s_rfd == fd_r)
{
break ;
}else
{
pPre = pCur ;
pCur = pCur ->s_next ;
}
}
if(pPre == NULL)
{
*phead = pCur ->s_next ;
free(pCur);
pCur = NULL ;
}else
{
pPre ->s_next = pCur ->s_next ;
free(pCur);
pCur = NULL;
}
}
task.c
/*************************************************************************
> File Name: ./src/task.c
> Author: KrisChou
> Mail:zhoujx0219@163.con
> Created Time: Tue 26 Aug 2014 07:38:11 PM CST
************************************************************************/
#include "server.h"
static int que_empty(pQUEUE pq)
{
return pq -> s_front == pq -> s_tail ;
}
static int que_full(pQUEUE pq)
{
return (pq -> s_tail + 1)%(TASK_CNT + 1) == pq -> s_front ;
}
static int que_cnt(pQUEUE pq)
{
return (pq ->s_tail - pq ->s_front + TASK_CNT + 1)%(TASK_CNT + 1) ;
} void add_task(pQUEUE pq,pTASK pt )
{
pthread_mutex_lock(&mutex);
while(que_full(pq))
{
pthread_cond_wait(&cond_master, &mutex);
}
pq ->s_arr[pq ->s_tail] = *pt ;
pq ->s_tail = (pq ->s_tail + 1)%(TASK_CNT + 1) ;
//if(que_cnt(pq) == 1)
{
pthread_cond_broadcast(&cond_slave);
}
pthread_mutex_unlock(&mutex);
sleep(1);
} void get_task(pQUEUE pq, pTASK pt)
{
pthread_mutex_lock(&mutex);
while(que_empty(pq))
{
pthread_cond_wait(&cond_slave, &mutex);
}
*pt = (pq ->s_arr)[pq ->s_front] ;
pq -> s_front = (pq -> s_front + 1)%(TASK_CNT + 1);
// if(que_cnt(pq) == TASK_CNT - 1)
{
pthread_cond_broadcast(&cond_master);
}
pthread_mutex_unlock(&mutex);
sleep(1); } void excute_task(pTASK pt)
{
write(pt ->s_fd, pt ->s_msg, strlen(pt ->s_msg));
}
客户端测试client.c
/*************************************************************************
> File Name: client.c
> Author: KrisChou
> Mail: zhoujx0210@163.com
> Created Time: Tue 26 Aug 2014 08:56:18 PM CST
************************************************************************/ #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
int main(int argc, char* argv[])//exe fifo
{
int fd_server, fd_snd, fd_recv ;
char rname[32], wname[32];
fd_server = open(argv[1], O_WRONLY);
memset(rname, 0, 32);
memset(wname, 0, 32);
sprintf(rname,"r.%d", getpid());
sprintf(wname,"w.%d", getpid());
mkfifo(rname,0666);
mkfifo(wname,0666);
char msg[1024]="";
sprintf(msg,"%d\n", getpid());
write(fd_server, msg, strlen(msg));
fd_recv = open(rname,O_RDONLY);
fd_snd = open(wname,O_WRONLY);
while(memset(msg, 0, 1024), fgets(msg, 1024, stdin) != NULL)
{
write(fd_snd, msg, strlen(msg));
memset(msg, 0, 1024);
read(fd_recv, msg, 1024);
write(1, msg, strlen(msg));
}
close(fd_server);
close(fd_snd);
close(fd_recv); return 0 ;
}
Makefile
SRC_DIR := ./src
INC_DIR := ./include
EXE_DIR := ./bin
CC := gcc
CFLAGS := -g -o
SRC_OBJECTS := $(wildcard $(SRC_DIR)/*.c)
INC_OBJECTS := $(wildcard $(INC_DIR)/*.h)
$(EXE_DIR)/main : $(SRC_OBJECTS) $(INC_OBJECTS)
$(CC) $(CFLAGS) $@ $(SRC_OBJECTS) -I$(INC_DIR) -lpthread
Linux多线程之同步3的更多相关文章
- Linux多线程与同步
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 典型的UNIX系统都支持一个进程创建多个线程(thread).在Linux进程基础 ...
- Linux多线程之同步2 —— 生产者消费者模型
思路 生产者和消费者(互斥与同步).资源用队列模拟(要上锁,一个时间只能有一个线程操作队列). m个生产者.拿到锁,且产品不满,才能生产.当产品满,则等待,等待消费者唤醒.当产品由空到不空,通知消费者 ...
- Linux多线程之同步
引言 条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待条件变量的条件成立而挂起(此时不再占用cpu):另一个线程使条件成立(给出条件成立信号).为了防止竞争,条件变 ...
- 【Linux多线程】同步与互斥的区别
同步与互斥这两个概念经常被混淆,所以在这里说一下它们的区别. 一.同步与互斥的区别 1. 同步 同步,又称直接制约关系,是指多个线程(或进程)为了合作完成任务,必须严格按照规定的 某种先后次序来运行. ...
- Linux 多线程信号量同步
PV原子操作 P操作: 如果有可用的资源(信号量值>0),则此操作所在的进程占用一个资源(此时信号量值减1,进入临界区代码); 如果没有可用的资源(信号量值=0),则此操作所在的进程被阻塞直到系 ...
- Python标准库08 多线程与同步 (threading包)
Python主要通过标准库中的threading包来实现多线程.在当今网络时代,每个服务器都会接收到大量的请求.服务器可以利用多线程的方式来处理这些请求,以提高对网络端口的读写效率.Python是一种 ...
- 转载自~浮云比翼:Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)
Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥) 介绍:什么是线程,线程的优点是什么 线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可 ...
- Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)
介绍:什么是线程,线程的优点是什么 线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可以看作是Unix进程的表亲,同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间, ...
- Linux多线程——使用互斥量同步线程
前文再续,书接上一回,在上一篇文章: Linux多线程——使用信号量同步线程中,我们留下了一个如何使用互斥量来进行线程同步的问题,本文将会给出互斥量的详细解说,并用一个互斥量解决上一篇文章中,要使用两 ...
随机推荐
- hdu 1509 Windows Message Queue
题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=1509 Windows Message Queue Description Message queue ...
- 使用 RestEasy 和 Apache Tomcat 构建 RESTful Web 服务
第一次,用这个RestEasy框架,用的时候,总是提示,404的错误,郁闷,呵呵,不过经过努力,终于解决问题,特别留个标记. 关于404的错误,上网找了一大堆,也还不行. 我感觉应该是lib下面架包的 ...
- 【热门收藏】iOS开发人员必看的精品资料(100个)——下载目录
iPhone.iPad产品风靡全球,巨大的用户群刺激着iOS软件开发需求,然而国内人才缺口很大,正处于供不应求的状态,ios开发前景大好.我们整理了51CTO下载中心100份热门的ios开发资料,做了 ...
- 两个有用的shell工具总结
shell工具之一:sed sed基础 sed编辑器被称作流编辑器,与常见的交互式文本编辑器刚好相反.文本编辑器可以通过键盘来交互式地插入.删除.替换文本中的数据:而流编辑器是基于一组预先的规则来编辑 ...
- [转]AngularJS: 使用Scope时的6个陷阱
在使用AngularJS中的scope时,会有6个主要陷阱.如果你理解AngularJS背后的概念的话,这6个点其实非常的简单.但是在具体讲述这6个陷阱之前我们先要讲两个其它的概念. 概念1: 双向数 ...
- Android实现入门界面布局
Android实现入门界面布局 开发工具:Andorid Studio 1.3 运行环境:Android 4.4 KitKat 代码实现 首先是常量的定义,安卓中固定字符串应该定义在常量中. stri ...
- JAVA内部类(转)
源出处:JAVA内部类 在java语言中,有一种类叫做内部类(inner class),也称为嵌入类(nested class),它是定义在其他类的内部.内部类作为其外部类的一个成员,与其他成员一样, ...
- pietty and putty safe password
如何让putty记住密码..pietty也一样的不能记住密码. 找不到好的的方法...只好试着按照参数格式做了一个快捷方式..F:\soft\pietty.exe -pw password123 ro ...
- SpringMVC处理脚本,SQL注入问题
SpringMVC处理脚本,SQL注入问题(写的不好勿喷,互相学习) 使用 Filter 来过滤浏览器发出的请求,对每个URI参数请求过滤些关键字,替换成安全的字符.所有请求的 getParamete ...
- C#委托详解(3):委托的实现方式大全(续)
接上篇(C#委托详解(2):实现方式大全),本篇继续介绍委托的实现方式. 4.Action<T>和Func<T>委托 使用委托时,除了为每个参数和返回类型定义一个新委托类型之外 ...