陕西交通建设网站,wordpress主题 产品展示,建设网站简单吗,在线编程网站开发目录
1--多线程的优点
2--进程和线程的差异
3--线程创建
4--线程使用
5--线程安全问题
6--互斥量
7--信号量
8--线程销毁
9--多线程并发聊天程序
9-1--服务器端
9-2--客户端
9-3--测试结果 1--多线程的优点 多进程服务器的缺点#xff1a; ① 创建进程的过程会带来…目录
1--多线程的优点
2--进程和线程的差异
3--线程创建
4--线程使用
5--线程安全问题
6--互斥量
7--信号量
8--线程销毁
9--多线程并发聊天程序
9-1--服务器端
9-2--客户端
9-3--测试结果 1--多线程的优点 多进程服务器的缺点 ① 创建进程的过程会带来一定的开销 ② 为了完成进程间的数据交换需要特殊的 IPC 技术 ③ 进程间的上下文切换是创建进程时的最大开销 多线程的优点 ① 线程的创建和上下文切换比进程的创建和上下文切换更快 ② 线程间交换数据时无需特殊技术 2--进程和线程的差异 每个进程拥有独立的内存空间拥有自己的数据区、堆区域和栈区域 每个线程只拥有自己的栈区域线程间共享数据区和堆区域因此线程间上下文切换时不需要切换数据区和堆可以利用数据区和堆区域交换数据 进程在操作系统构成单独执行流的单位 线程在进程构成单独执行流的单位 进程是在操作系统内部生成多个执行流线程就是在同一进程内部创建多条执行流 3--线程创建
#include pthread.h
int pthread_create(pthread_t* restrict thread, const pthread_attr_t* restrict attr, void* (* start_routine)(void*), void* restrict arg);
// 成功时返回 0失败时返回其他值
// thread 表示保存新创建线程 ID 的变量地址值
// attr 表示用于传递线程属性的参数传递 NULL 时表示创建默认属性的线程
// start_routine 表示线程的入口函数
// arg 表示传递给线程入口函数的参数
// gcc thread1.c -o thread1 -lpthread
// ./thread1#include stdio.h
#include pthread.h
#include unistd.hvoid* thread_main(void* arg){ // 线程入口函数int i;int cnt *((int*)arg);for(i 0; i cnt; i){sleep(1);puts(running thread);}return NULL;
}int main(int argc, char* argv[]){pthread_t t_id;int thread_param 5;if(pthread_create(t_id, NULL, thread_main, (void*)thread_param) ! 0){ // 创建线程puts(pthread_create() error);return -1;}sleep(10);puts(end of main);return 0;
} 4--线程使用 调用 pthread_join(ID) 可以使进程或线程进入等待状态直到 ID 对应的线程终止为止 #include pthread.h
int pthread_join(pthread_t thread, void** status);
// 成功时返回0失败时返回其他值
// thread 表示线程ID只有该线程终止后才会从函数返回
// status 表示保存线程返回值的指针变量地址值
// gcc thread2.c -o thread2 -lpthread
// ./thread2#include stdio.h
#include stdlib.h
#include string.h
#include pthread.h
#include unistd.hvoid* thread_main(void *arg){int i;int cnt *((int*)arg);char* msg (char*)malloc(sizeof(char)*50);strcpy(msg, Hello, Im thread~ \n);for(i 0; i cnt; i){sleep(1);puts(running thread);}return (void*)msg;
}int main(int argc, char* argv[]){pthread_t t_id;int thread_param 5;void* thr_ret;// 创建线程if(pthread_create(t_id, NULL, thread_main, (void*)thread_param) ! 0){puts(pthread_create() error);return -1;}// 阻塞等待线程返回if(pthread_join(t_id, thr_ret) ! 0){puts(pthread_join() error);return -1;}// 打印线程返回值printf(Thread return message: %s \n, (char*)thr_ret);free(thr_ret);return 0;
} 5--线程安全问题 多个线程同时调用函数执行临界区代码时会出现问题 根据临界区是否引起问题函数可分为线程安全函数和非线程安全函数 线程安全函数被多个线程同时调用时不会引发问题非线程安全函数被同时调用时会引发问题 以下代码展示了多个线程同时访问临界区代码操作全局变量时会出现意向不到的问题 // gcc thread4.c -D_REENTRANT -o thread4 -lpthread
// ./thread4#include stdio.h
#include unistd.h
#include stdlib.h
#include pthread.h
#define NUM_THREAD 100long long num 0;void* thread_inc(void* arg){int i;for(i 0; i 50000000; i){num 1;}return 0;
}void* thread_des(void* arg){int i;for(i 0; i 50000000; i){num - 1;}return 0;
}int main(int argc, char* argv[]){pthread_t thread_id[NUM_THREAD];int i;printf(sizeof long long: %ld \n, sizeof(long long));for(i 0; i NUM_THREAD; i){// 各创建50个线程分别执行对全局变量 num 的加减操作if(i%2){pthread_create((thread_id[i]), NULL, thread_inc, NULL);}else{pthread_create((thread_id[i]), NULL, thread_des, NULL);}}for(i 0; i NUM_THREAD; i){pthread_join(thread_id[i], NULL);}printf(result: %lld \n, num);return 0;
} 正常结果应为0但实际结果并不是这就是多线程同时访问临界区会出现数据竞争等的问题 线程访问变量 num 时应阻止其他线程访问直到一个线程完成运算这就是同步Synchronization线程同步用于解决线程访问顺序引发的问题 6--互斥量 互斥量表示不允许多个线程同时访问主要用于解决线程同步访问的问题 #include pthread.h
int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr);
int pthread_mutex_destroy(pthread_mutex_t* mutex);
// 成功时返回 0失败时返回其他值
// mutex 表示创建和销毁互斥量时传递保存和销毁互斥量的变量地址值
// attr 传递即将创建的互斥量属性没有特别需要指定的属性时传递 NULLint pthread_mutex_lock(pthread_mutex_t* mutex); // 上锁
int pthread_mutex_unlock(pthread_mutex_t* mutex); // 解锁
// 成功时返回 0失败时返回其他值pthread_mutex_t mutex;
pthread_mutex_lock(mutex);
// 临界区开始
// ...
// 临界区结束
pthread_mutex_unlock(mutex);
7--信号量 利用二进制信号量完成控制线程程序为中心的同步方法 #include semaphore.h
int sem_init(sem_t* sem, int pshared, unsigned int value);
int sem_destroy(sem_t* sem);
// 成功时返回 0失败时返回其他值
// sem 表示信号量的变量地址值
// pshared 传递其他值时创建可由多个进程共享的信号量传递 0 时创建只允许一个进程内部使用的信号量
// value 表示指定新创建的信号量的初始值int sem_post(sem_t* sem); // 信号量增加1
int sem_wait(sem_t* sem); // 信号量减少1
// 成功时返回 0失败时返回其他值sem_wait(sem); // 信号量变为0
// 临界区的开始
// ...
// 临界区的结束
sem_post(sem); // 信号量变为1
8--线程销毁 线程销毁的 3 种方法 ① 调用 main 函数的返回语句 ② 调用 pthread_join() 函数 ③ 调用 pthread_detach() 函数 9--多线程并发聊天程序
9-1--服务器端
// gcc chat_server.c -D_REENTRANT -o chat_server -lpthread
// ./chat_server 9190#include stdio.h
#include string.h
#include stdlib.h
#include unistd.h
#include arpa/inet.h
#include sys/socket.h
#include netinet/in.h
#include pthread.h#define BUF_SIZE 100
#define MAX_CLNT 256int clnt_cnt 0;
int clnt_socks[MAX_CLNT];
pthread_mutex_t mutx;void send_msg(char* msg, int len){int i;pthread_mutex_lock(mutx);for(i 0; i clnt_cnt; i){write(clnt_socks[i], msg, len);}pthread_mutex_unlock(mutx);
}void* handle_clnt(void* arg){int clnt_sock *((int*)arg);int str_len 0, i;char msg[BUF_SIZE];while((str_len read(clnt_sock, msg, sizeof(msg))) ! 0){send_msg(msg, str_len);}pthread_mutex_lock(mutx);for(i 0; i clnt_cnt; i){if(clnt_sock clnt_socks[i]){while(i clnt_cnt - 1)clnt_socks[i] clnt_socks[i1];break;}}clnt_cnt--;pthread_mutex_unlock(mutx);close(clnt_sock);return NULL;
}void error_handling(char *msg){fputs(msg, stderr);fputc(\n, stderr);exit(1);
}int main(int argc, char* argv[]){int serv_sock, clnt_sock;struct sockaddr_in serv_adr, clnt_adr;int clnt_adr_sz;pthread_t t_id;if(argc ! 2){printf(Usage: %s port\n, argv[0]);exit(1);}pthread_mutex_init(mutx, NULL);serv_sock socket(PF_INET, SOCK_STREAM, 0);memset(serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family AF_INET;serv_adr.sin_addr.s_addr htonl(INADDR_ANY);serv_adr.sin_port htons(atoi(argv[1]));if(bind(serv_sock, (struct sockaddr*)serv_adr, sizeof(serv_adr)) -1){error_handling(bind() error);}if(listen(serv_sock, 5) -1){error_handling(listen() error);}while(1){clnt_adr_sz sizeof(clnt_adr);clnt_sock accept(serv_sock, (struct sockaddr*)clnt_adr, clnt_adr_sz);pthread_mutex_lock(mutx);clnt_socks[clnt_cnt] clnt_sock;pthread_mutex_unlock(mutx);pthread_create(t_id, NULL, handle_clnt, (void*)clnt_sock);pthread_detach(t_id);printf(Connected client IP: %s \n, inet_ntoa(clnt_adr.sin_addr));}close(serv_sock);return 0;
}9-2--客户端
// gcc chat_client.c -D_REENTRANT -o chat_client -lpthread
// ./chat_client 127.0.0.1 9190 Yoon#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include arpa/inet.h
#include sys/socket.h
#include pthread.h#define BUF_SIZE 100
#define NAME_SIZE 20char name[NAME_SIZE] [DEFAULT];
char msg[BUF_SIZE];void* send_msg(void* arg){int sock *((int*)arg);char name_msg[NAME_SIZE BUF_SIZE];while(1){fgets(msg, BUF_SIZE, stdin);if(!strcmp(msg, q\n) || !strcmp(msg, Q\n)){close(sock);exit(0);}sprintf(name_msg, %s %s, name, msg);write(sock, name_msg, strlen(name_msg));}return NULL;
}void* recv_msg(void* arg){int sock *((int*)arg);char name_msg[NAME_SIZEBUF_SIZE];int str_len;while(1){str_len read(sock, name_msg, NAME_SIZEBUF_SIZE-1);if(str_len -1){return (void*)-1;}name_msg[str_len] 0;fputs(name_msg, stdout);}return NULL;
}void error_handling(char *msg){fputs(msg, stderr);fputc(\n, stderr);exit(1);
}int main(int argc, char* argv[]){int sock;struct sockaddr_in serv_addr;pthread_t snd_thread, rcv_thread;void* thread_return;if(argc ! 4){printf(Usage: %s IP port name\n, argv[0]);exit(1);}sprintf(name, [%s], argv[3]);sock socket(PF_INET, SOCK_STREAM, 0);memset(serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family AF_INET;serv_addr.sin_addr.s_addr inet_addr(argv[1]);serv_addr.sin_port htons(atoi(argv[2]));if(connect(sock, (struct sockaddr*)serv_addr, sizeof(serv_addr)) -1){error_handling(connect() error!);}pthread_create(snd_thread, NULL, send_msg, (void*)sock);pthread_create(rcv_thread, NULL, recv_msg, (void*)sock);pthread_join(snd_thread, thread_return);pthread_join(rcv_thread, thread_return);close(sock);return 0;
}
9-3--测试结果