上篇验证一些问题时发现线程操作方面存在不少疑点,这两天上网查阅了一些博文,对多线程操作有了个大致的了解。
线程操作上常用的函数莫过于pthread_create与pthread_join两个函数,前面一直疑惑的一些问题大概知道了原因。当pthread_create创建线程以后,如果不执行join阻塞主线程(主线程通过其它方式阻塞等待效果也一样),那么上篇例子中就会出现新建立的线程还没来得及执行主线程就已经结束了,所以当时怀疑create是否不执行线程体函数,这个理解其实是错误的。而调用join函数以后,主线程将会被阻塞等待新建线程的结束,从而使得新线程有足够的时间来运行。查阅资料时获得一段信息摘抄在此:
线程的本质:在Linux中,新建线程并不是在原先的进程中,而是系统通过一个系统调用clone()。该系统调用copy了一个和原先进程完全一样的进程,并在这个进程中执行线程函数。不过这个copy过程和fork不一样。copy后的进程和原先的进程共享了所有的变量及运行环境,这样原先进程中变量变动在copy后的新进程中便能体现出来。
另外如果主线程,也就是main函数执行的那个线程,在其它线程退出之前就已经退出,那么带来的bug将不可估量,通过join函数来让主线程阻塞,知道所有线程都已经退出。关于线程的退出还需要继续查阅资料,线程函数体执行完就自动退出?还是需要执行exit等类似的函数?
下面是一个简单的示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h> // About file operation
#include <pthread.h> // About thread operation
#define MAX(x,y) (((x)>(y)) ? (x) : (y))
static pthread_t fd_test_thread_id;
static pthread_t thread_test_thread_id;
static pthread_t thread_test1_thread_id;
static fd,fd1;
void *fd_test(void *data)
{
fd_set fd_fds;
int fd_nfds = 0;
int rc;
static int test_num = 0;
pid_t pid;
pid = getpid();
printf("%s [E] \n",__func__);
fd1 = open("./test.txt", O_CREAT|O_RDONLY); // 已只读模式打开当前目录下的test.txt文件。O_CREAT文件若不存在就新建。
if(fd1 < 0)
{
printf("Open test error! pid=%d \n", pid);
}
else
{
printf("test fd1:%d ! pid=%d \n", fd1, pid); // 这里验证文件描述符为4。
}
do{
test_num++;
struct timeval fd_timeout;
fd_timeout.tv_usec = 0;
fd_timeout.tv_sec = 5;
FD_ZERO(&fd_fds);
FD_SET(fd, &fd_fds);
FD_SET(fd1, &fd_fds);
fd_nfds = MAX(fd, fd1);
printf("here fd=%d, fd1=%d, fd_nfds=%d!\n", fd, fd1, fd_nfds);
rc = select(fd_nfds+1, &fd_fds, NULL, NULL, &fd_timeout);
if(rc == 0)
{
printf("fd thread select timeout! \n");
continue;
}
else if(rc < 0)
{
printf("Select error! \n");
continue;
}
else if(rc)
{
printf("Something select! \n");
if(FD_ISSET(fd, &fd_fds))
{ // There have noting happend in fd, why function is running here?
printf("Select fd occour! \n");
}
if(FD_ISSET(fd1, &fd_fds))
{ // There have noting happend in fd1, why function is running here?
printf("Select fd1 occour! \n");
}
}
// sleep(2);
}while(test_num<3);
printf("%s [X] \n", __func__);
pthread_exit((void *)3);
}
void *thread_test1(void *data)
{
int i = 0;
printf("%s [E] \n", __func__);
do{
i++;
printf("Thread test1 here!! i=%d \n", i);
sleep(2);;
}while(i<5);
printf("%s [X] \n", __func__);
}
void *thread_test(void *data)
{
int i = 0;
int err;
printf("%s [E] \n", __func__);
err = pthread_create(&thread_test1_thread_id, NULL, thread_test1, NULL); // Creat a new thread, but not running?
if(err != 0)
{
printf("Can't create thread_test1 thread! \n");
}
do{
i++;
printf("Thread test here! i=%d \n", i);
sleep(2);
}while(i<3);
printf("%s [X] \n", __func__);
pthread_exit((void *)3); // 此线程运行结束以后(线程是否退出结束?),thread_test1还在继续执行。
}
int main(void)
{
int err;
int ret_val;
pid_t pid;
pid = getpid();
fd = open("./tmp.txt", O_CREAT|O_RDONLY); // 已只读模式打开当前目录下的tmp.txt文件。O_CREAT文件若不存在就新建。
if(fd < 0)
{
printf("Open error! pid=%d \n", pid);
}
else
{
printf("fd:%d ! pid=%d \n", fd, pid); // 这里验证文件描述符为3。
}
err = pthread_create(&fd_test_thread_id, NULL, fd_test, NULL); // Creat a new thread, but not running?
if(err != 0)
{
printf("Can't create fd_test thread! \n");
}
sleep(8); // 这里阻塞等fd_test线程函数体运行结束,但是通过LOG发现join函数返回还是0,原因尚不清楚。
printf("thread join! \n");
if(pthread_join(fd_test_thread_id, (void**)&ret_val)){ // Joint this thread and running it.
printf("Join test thread failed! \n");
}
printf("thread join return:%d \n", ret_val);
err = pthread_create(&thread_test_thread_id, NULL, thread_test, NULL); // Creat a new thread, but not running?
if(err != 0)
{
printf("Can't create thread_test thread! \n");
}
sleep(30);
printf("Main exit! \n");
return 0;
}