多线程
●相关函数
1.pthread_create
独立线程不需要释放掉,执行完自动结束
pthread_create只能接受一个参数,当要传入多个参数时,可以用结构体来传参
2.pthread_join
如果忘记使用pthread_join来收回线程,线程占用的资源就无法回收,类似于用malloc没有free
3.pthread_mutex_lock
用来避免访问冲突,在某一时刻只有唯一的线程可以对变量进行访问。
4.pthread_mutex_unlock
5.pthread_cond_wait
6.pthread_cond_signal
●线程间通信实例:同时统计两个文件的字数,先统计完的显示,两个都统计完就结束程序
使用了编程设备的三项:容器、旗帜和锁。一个变量保存数据,一个条件对象和一个互斥量。
原线程启动了两个计数线程然后开始等待结果的到来。当一个线程完成计数后,线程通过把指针存入邮箱变量的方式来传递结果。首先线程对邮箱加锁,然后检查邮箱,如果邮箱非空即主线程还未取出,线程就把锁打开等待取出后信号的到来,之后线程再一次锁住邮箱,把结果放入邮箱,然后发出信号并解锁。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <ctype.h>
struct arg_set { //要传递两个参数给线程,使用结构体传参
char *fname;
int count;
};
struct arg_set *mailbox = NULL; //邮箱
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; //互斥锁
pthread_cond_t flag = PTHREAD_COND_INITIALIZER; //条件变量
void *count_words(void *);
int main(int argc, char *argv[])
{
pthread_t t1, t2;
struct arg_set args1, args2;
int reports_in = 0; //主线程收到的结果数量
int total_words = 0;
if ( argc != 3 ){
printf("usage: %s file1 file2\n", argv[0]);
exit(1);
}
args1.fname = argv[1];
args1.count = 0;
pthread_create(&t1, NULL, count_words, (void *) &args1);
args2.fname = argv[2];
args2.count = 0;
pthread_create(&t2, NULL, count_words, (void *) &args2);
pthread_mutex_lock(&lock); //先将锁锁住,方便调用cond_wait
while( reports_in < 2 ){
printf("MAIN: waiting for flag to go up\n");
/*函数打开锁,并阻塞当前线程等待flag变化*/
pthread_cond_wait(&flag, &lock);
/*flag改变,主线程从邮箱取数据*/
printf("MAIN: Wow! flag was raised, I have the lock\n");
printf("%7d: %s\n", mailbox->count, mailbox->fname);
total_words += mailbox->count;
if ( mailbox == &args1)
pthread_join(t1,NULL);
if ( mailbox == &args2)
pthread_join(t2,NULL);
mailbox = NULL;
/*改变flag,告诉其它线程已经取完了*/
pthread_cond_signal(&flag);
reports_in++;
}
pthread_mutex_unlock(&lock);
printf("%7d: total words\n", total_words);
}
void *count_words(void *a)
{
struct arg_set *args = a;//将void*型变量转变回去
FILE *fp;
int c, prevc = '\0';
if ( (fp = fopen(args->fname, "r")) != NULL ){
while( ( c = getc(fp)) != EOF ){
/*当前不为字符数字,前一个是字符数字,count就增1*/
if ( !isalnum(c) && isalnum(prevc) )
args->count++;
prevc = c;
}
fclose(fp);
} else
perror(args->fname);
printf("COUNT: waiting to get lock\n");//统计完,准备关闭锁
pthread_mutex_lock(&lock);//关闭锁
printf("COUNT: have lock, storing data\n");
/*主线程还未从邮箱取出上一个报告,阻塞直到主线程取出*/
if ( mailbox != NULL ){
printf("COUNT: oops..mailbox not empty. wait for signal\n");
pthread_cond_wait(&flag,&lock);
}
mailbox = args;
printf("COUNT: raising flag\n");
pthread_cond_signal(&flag); //发//函数打开锁,并阻塞当前线程等待flag变化
送信号,告诉主线程来取
printf("COUNT: unlocking box\n");
pthread_mutex_unlock(&lock);//关闭锁
return NULL;
}
●多线程web服务器
主循环
while (1) {
fd = accept(sock, NULL, NULL);
server_requests++;
fdpttr = malloc(sizeof(int));
*fdptr = fd;
pthread_create(&worker, &attr, handle_call, fdptr);//将fd作为参数传入
}
/* twebserv.c - a threaded minimal web server (version 0.2)
* usage : tws portnumber
* features : supports the GET command only
* runs in the current directory
* creates a thread to handle each request
* supports a special status URL to report internal state
* building : cc twebserv.c socklib.c -lpthread -o twebserv
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <time.h>
/* server facts here */
time_t server_started;
int server_bytes_sent;
int server_requests;
main(int ac, char *av[])
{
int sock, fd;
int *fdptr;
pthread_t worker;
pthread_attr attr;
void *handle_call(void *);
if (ac == 1) {
fprintf(stderr, "usage: tws portnum\n");
exit(1);
}
sock = make_server_socket(atoi(av[1]));
if (sock == -1) { perror("making socket"); exit(2); }
setup(&attr);
/* main loop here: take call, handle call in new thread */
while (1) {
fd = accept(sock, NULL, NULL);
server_requests++;
fdpttr = malloc(sizeof(int));
*fdptr = fd;
pthread_create(&worker, &attr, handle_call, fdptr);
}
}
/*
* initialize the status variables and
* set the thread attribute to detached
*/
setup(pthread_attr_t *attrp)
{
pthread_attr_init(attrp);
pthread_attr_setdetachstate(attrp, PTHREAD_CREATE_DETACHED);
time(&server_started);
server_requests = 0;
server_bytes_sent = 0;
}
void *handle_call(void *fdptr)
{
FILE *fpin;
char request[BUFSIZ];
int fd;
fd = *(int *)fdptr;
free(fdptr); /* get fd from arg */
fpin = fdopen(fd, "r"); /* buffer input */
fgets(request, BUFSIZ, fpin); /* read client request */
printf("got a call on %d: request = %s", fd, request);
skip_rest_of_header(fpin);
process_rq(request, fd); /* process client rq */
fclose(fpin);
}
/*------------------------------------------------------------------------------*
skip_rest_of_header(FILE *)
skip over all request info until a CRNL is seen
-----------------------------------------------------------------------------*/
skip_rest_of_header(FILE *fp)
{
char buf[BUFSIZ] = [];
while (fgets(buf, BUFSIZ, fp) != NULL && strcmp(buf, "\r\n,") != 0)
;
}
/*------------------------------------------------------------------------------*
process_rq(char *rq, int fd)
do what the request asks for and write reply to fd
handles request in a new process
rq is HTTP command: GEt /foo/bar.html HTTP/1.0
-----------------------------------------------------------------------------*/
process_rq(char *rq, int fd)
{
char cmd[BUFSIZ], arg[BUFSIZ];
if (sscanf(rq, "%s%s", cmd, arg) != 2)
return;
sanitize(arg);
printf("sanitized version is %s\n", arg);
if (strcmp(cmd, "GET") != 0)
not_implemented();
else if (built_in(arg, fd))
;
else if (not_exist(arg))
do_404(arg, fd);
else if (isadir(arg))
do_ls(arg, fd);
else
do_cat(arg, fd);
}
/*
* make sure all paths are below the current directory
*/
sanitize(char *str)
{
char *src, *dest;
src = dest = str;
while (*src) {
if (strcmp(src, "/../", 4) == 0)
src += 3;
else if (srncmp(src, "//", 2) == 0)
src++;
else
*dest++ = *src++;
}
*dest = '\0';
if (*str == '/')
strcpy(str, str + 1);
if (str[0] == '\0' || strcmp(str, "./") == 0 || strcmp(str, "./..") == 0)
strcpy(str, ".");
}
/* handle built - in URLs here. Only one so far is "status" */
built_in(char *arg, int fd)
{
FILE *fp;
if (strcmp(arg, "status") != 0)
return 0;
http_reply(fd, &fp, 200, "OK", "text/plain", NULL);
fprintf(fp, "Server started: %s", ctime(&server_started));
fprintf(fp, "Total requests: %d\n", server_requests);
fprintf(fp, "Bytes sent out: %d\n", server_bytes_sent);
fclose(fp);
return 1;
}
http_reply(int fd, FILE **fpp, int code, char *msg, char *type, char *content)
{
FILE *fp = fdopen(fd, "w");
int bytes = 0;
if (fp != NULL) {
bytes = fprintf(fp, "HTTP/1.0 %d %s\r\n", code, msg);
bytes != fprintf(fp, "Content-type: %s\r\n\r\n", type);
if (content)
bytes += fprintf(fp, "%s\r\n", content);
}
fflush(fp);
if (fpp)
*fpp = fp;
else
fclose(fp);
return bytes;
}
/*------------------------------------------------------------------------------*
simple functions first:
not_implemented(fd) unimplemented HTTP command
and do_404(item, fd) no such object
-----------------------------------------------------------------------------*/
not_implemented(int fd)
{
http_reply(fd, NULL, 501, "Not not_implemented", "text/plain", "That command is not not_implemented");
}
do_404(char *item, int fd)
{
http_reply(fd, NULL, 404, "Not Found", "text/plain", "The item you seek is not here");
}
/*------------------------------------------------------------------------------*
the directory listing section
isadir() uses stat, not_exist() uses stat
-----------------------------------------------------------------------------*/
isadir(char *f)
{
struct stat info;
return (stat(f, &info) != -1 &7 S_ISDIR(info.st_mode));
}
not_exist(char *f)
{
struct stat info;
return (stat(f, info) != -1);
}
do_ls(char *dir, int fd)
{
DIR *dirptr;
struct dirent *direntp;
FILE *fp;
int bytes = 0;
bytes = http_reply(fd, &fp, 200, "OK", "text/plian", NULL);
bytes += fprintf(fp, "Listing of Directory %s\n", dir);
if ((dirptr = opendir(dir)) != NULL) {
while (direntp = readdir(dirptr)) {
bytes += fprintf(fp, "%s\n", direntp->d_name);
}
closedir(dirptr);
}
fclose(fp);
server_bytes_sent += bytes;
}
/*------------------------------------------------------------------------------*
functions to cat files here.
file_type(filename) returns the 'extension': cat uses it
-----------------------------------------------------------------------------*/
char *file_type(char *f)
{
char *cp;
if ((cp = strrchr(f, '.')) != NULL)
return cp + 1;
return " -";
}
/* do_cat(filename, fd): sends header then the contents */
do_cat(char *f, int fd)
{
char *extension = file_type(f);
char *type = "text/plain";
FILE *fpsock, *fpfile;
int c;
int bytes = 0;
if (strcmp(extension, "html") == 0)
type = "text/html";
else if (strcmp(extension, "gif") == 0)
type = "image/gif";
else if (strcmp(extension, "jpg") == 0)
type = "image/jpeg";
else if (strcmp(extension, "jpeg") == 0)
type = "image/jpeg";
fpsock = fdopen(fd, "w");
fpfile = fopen(f, "r");
if (fpsock != NULL && fpfile != NULL)
{
bytes = http_reply(fd, &fpsock, 200, "OK", type, NULL);
while ((c = getc(fpfile)) != EOF) {
putc(c, fpsock);
bytes++;
}
fclose(fpfile);
fclose(fpsock);
}
server_bytes_sent += bytes;
}