创建一个新的线程
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
thread:返回线程ID
attr:设置线程的属性,attr为NULL表示使用默认属性。
start_toutine:是个函数地址,线程启动后要执行的函数
arg:传给线程启动函数的参数。
成功返回0,失败返回错误码;
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <unistd.h> #include <sys/types.h> #define ERR_EXIT(m) \ do { \ perror(m);\ exit(EXIT_FAILURE);\ }while(0) void* therad_routine(void *arg) { int i; for (i = 0; i < 20; ++i) { /* code */ printf("B"); fflush(stdout); } return 0; } int main(int argc, const char *argv[]) { pthread_t tid; int ret; ret = pthread_create(&tid, NULL, therad_routine, NULL); if(ret != 0) { fprintf(stderr, "pthread_create:%s\n", strerror(ret));\ exit(EXIT_FAILURE); } int i; for (i = 0; i < 20; ++i) { /* code */ printf("A"); fflush(stdout); usleep(20); } sleep(1); return 0; }
pthread同样也提供了线程内的而errno变量,一支持其他使用errno的代码,
对于pthreads函数的错误,简易通过返回值判定,因为赌气返回值要比赌气线程内的errno变量的开销更小。
打印结果
ABABABABABAB
可以看得出单核的情况下,主线程和线程之间是共享CPU时间片的。
它们是交替进行的。
ret = pthread_join(tid, NULL); if(ret != 0) { fprintf(stderr, "pthread_join:%s\n", strerror(ret)); exit(EXIT_FAILURE); }
主线程利用pthread_join来回收线程资源。
相对于多进程下僵尸进程的概念,多线程中也会产生僵尸线程。
所以我们可以使用pthread_detach来分离一个线程,这样便不会产生僵尸线程。
下面利用线程函数实现一个简单的回射服务器练习一下
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define ERR_EXIT(m) \ do { \ perror(m);\ exit(EXIT_FAILURE);\ }while(0) void* thread_routine(void* arg) { pthread_detach(pthread_self()); int conn = (int)arg; echo_sev(conn); printf("exiting thread ....\n"); return 0; } int main(int argc, const char *argv[]) { int listen = socket(PF_INET, SOCK_STREAM, 0); if(socket < 0 ) ERR_EXIT("socket"); struct sockaddr_in servraddr; servaddr.sin_family = AF_INET; servaddr.sin_port = htons(5188); servaddr.sin_addr.s_addr = htonl(INADDR_ANY); int on = 1; //设置端口复用 if(setsocketopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &ON, sizeof(on)) <0) ERR_EXIT("setsocketopt"); if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) <0) ERR_EXIT("bind"); if(listen(listenfd, SOMAXCONN)<0) ERR_EXIT("listen"); struct sockaddr_in peeraddr; socklen_t peerlen; int conn; while(1) { conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen); if(conn < 0) ERR_EXIT("accept"); printf("ip = %s, port = %d\n", inet_ntoa(peeraddr.sin_addr, ntohs(peeraddr.sin_port))); pthread_t tid; int ret; ret = pthread_create(&tid, NULL, thread_routine, (void*)conn); if(ret != 0) { fprintf(stderr, "pthread_create:%s\n", strerrno(ret)); exit(EXIT_FAILURE); } } return 0; }
注意点就是,要将创建的线程进行detrach,来避免僵尸线程的产生。
二。线程属性
通过设置pthread_create的第二个参数来来达到我们的但的目的。
分离属性的获取与设置
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
这个函数有两个参数,
PTHREAD_CREATE_DETACHED:
所以创建的线程都会是分离的。
PTHREAD_CREATE_JOINABLE:
所有新创建的线程都是不分离的。
实例代码如下
#include <pthread.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <string.h>
可以通过上述的几个函数来获取线程的属性。
线程除了属性,还有128个特定的指针可以用于保存TSD即线程特有数据
需要手动自己create,可以指向任意的数据类型
#include <pthread.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define ERR_EXIT(m) \ do { \ perror(m);\ exit(EXIT_FAILURE);\ }while(0) typedef struct tsd { pthreead_t tid; char *str; }tsd_t; pthread_key_t key_tsd; void destroy_routine(void *value) { printf("销毁。。。特定数据\n"); free(value); } void thread_routine(void *value) { tsd_t *value = (tsd_t*)malloc(sizeof(tsd_t)); value->tid = pthread_self(); value->str = (char*)arg; pthread_setspecific(key_tsd, value); printf("%ssetspecific%p\n", (char*)arg, value); value = pthread_getspecific(key_tsd); printf("tid=0x%x, str=%s\n", value->tid, value->str); sleep(2); return 0; } int main(int argc, const char *argv[]) { pthread_key_create(tsd, destroy_routine); pthread_t tid1; pthread_t tid2; pthread_create(&tid1, NULL, thread_routine, "thread1"); pthread_create(&tid2, NULL, thread_routine, "thread2"); pthread_join(&tid1, NULL); pthread_join(&tid2, NULL); pthread_key_delete(dkey_tsd); return 0; }
解析:
create函数会寻找一个数据空位。
pthread_key_create(tsd, destroy_routine);
线程退出后会自动调用destroy_routine指向的函数去销毁该TSD。
pthread_setspecific(key_tsd, value);
这个函数时将要保存的信息保存到放入我们申请的那个TSD中。
value = pthread_getspecific(key_tsd);
这个函数可以从TSD中获取到之前保存的数据。