论坛首页 编程语言技术论坛

C: Linux C 编程 - 线程终止

浏览 370 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2020-09-22  

线程终止

以下内容翻译的是:

 

http://man7.org/linux/man-pages/man3/pthread_create.3.html

 

The new thread terminates in one of the following ways:

       * It calls pthread_exit(3), specifying an exit status value that is
         available to another thread in the same process that calls
         pthread_join(3).

       * It returns from start_routine().  This is equivalent to calling
         pthread_exit(3) with the value supplied in the return statement.

       * It is canceled (see pthread_cancel(3)).

       * Any of the threads in the process calls exit(3), or the main thread
         performs a return from main().  This causes the termination of all
         threads in the process.

 

 

片段。 线程中断有以下几种方式:

线程中调用thread exit函数,该函数调用需要指定一个退出状态,如果有其他线程 thread join这个线程的话,这个状态值有用。

 

线程函数返回。这种方式等同于thread exit,exit的状态值等同于返回值。

 

 

调用thread cancel, 请求取消了这个线程。

 

在程序中任何地方、任何线程中调用了exit方法退出,包括在主线程main方法返回都将 中断所有线程。

 

请求取消线程

可以通过pthread_cancel函数请求取消线程。pthread_cancel函数会发送一个cancel请求给被取消线程。

 

另一个相关函数

pthread_testcancel

 

void pthread_testcancel(void);

请求投递pending状态的cancel请求。如果请求队列中有cancel请求的话,该函数请求cancel请求投递给被取消线程。如果请求队列中没有cancel请求, 调用该函数无影响。

 

另外,该函数将在线程中创建一个取消点(Cancellation points),这样的话,如果一个线程除了其他执行代码之外没有取消点(Cancellation points),将响应cancel请求。

 

被取消线程是否会响应pthread_cancel的cancel请求取决于被取消线程的两个控制属性(可撤销属性):state、type。state表示线程的可撤销状态,type表示线程的可撤销类型。

 

state可以是enabled (新创建的线程默认为enabled,表示可撤销)或者disabled。

 

不管state设置为enabled 还是disabled,都是调用pthread_cancel向线程发起cancel请求,如果disabled,发起的cancel请求将会保留在请求队列中,直到enabled可撤销状态state。

 

type可以是asynchronous或者deferred (新创建的线程默认为deferred)。如果type设置为asynchronous,表示取消操作是异步的,取消操作可能在任何时候被取消,通常会立即取消,但系统并不保证会立即取消,值得注意的是系统有时候还不保证最后就一定能取消成功。如果type设置为deferred,取消操作将延迟到被取消线程下次调用一个可以作为取消点(Cancellation points)的函数的时候。 

 

可以作为取消点(Cancellation points)的函数列表:

参考http://man7.org/linux/man-pages/man7/pthreads.7.html

   Cancellation points
       POSIX.1 specifies that certain functions must, and certain other
       functions may, be cancellation points.  If a thread is cancelable,
       its cancelability type is deferred, and a cancellation request is
       pending for the thread, then the thread is canceled when it calls a
       function that is a cancellation point.

       The following functions are required to be cancellation points by
       POSIX.1-2001 and/or POSIX.1-2008:

           accept()
           aio_suspend()
           clock_nanosleep()
           close()
           connect()
           creat()
           fcntl() F_SETLKW
           fdatasync()
           fsync()
           getmsg()
           getpmsg()
           lockf() F_LOCK
           mq_receive()
           mq_send()
           mq_timedreceive()
           mq_timedsend()
           msgrcv()
           msgsnd()
           msync()
           nanosleep()
           open()
           openat() [Added in POSIX.1-2008]
           pause()
           poll()
           pread()
           pselect()
           pthread_cond_timedwait()
           pthread_cond_wait()
           pthread_join()
           pthread_testcancel()
           putmsg()
           putpmsg()
           pwrite()
           read()
           readv()
           recv()
           recvfrom()
           recvmsg()
           select()
           sem_timedwait()
           sem_wait()
           send()
           sendmsg()
           sendto()
           sigpause() [POSIX.1-2001 only (moves to "may" list in POSIX.1-2008)]
           sigsuspend()
           sigtimedwait()
           sigwait()
           sigwaitinfo()
           sleep()
           system()
           tcdrain()
           usleep() [POSIX.1-2001 only (function removed in POSIX.1-2008)]
           wait()
           waitid()
           waitpid()
           write()
           writev()

       The following functions may be cancellation points according to
       POSIX.1-2001 and/or POSIX.1-2008:

           access()
           asctime()
           asctime_r()
           catclose()
           catgets()
           catopen()
           chmod() [Added in POSIX.1-2008]
           chown() [Added in POSIX.1-2008]
           closedir()
           closelog()
           ctermid()
           ctime()
           ctime_r()
           dbm_close()
           dbm_delete()
           dbm_fetch()
           dbm_nextkey()
           dbm_open()
           dbm_store()
           dlclose()
           dlopen()
           dprintf() [Added in POSIX.1-2008]
           endgrent()
           endhostent()
           endnetent()
           endprotoent()
           endpwent()
           endservent()
           endutxent()
           faccessat() [Added in POSIX.1-2008]
           fchmod() [Added in POSIX.1-2008]
           fchmodat() [Added in POSIX.1-2008]
           fchown() [Added in POSIX.1-2008]
           fchownat() [Added in POSIX.1-2008]
           fclose()
           fcntl() (for any value of cmd argument)
           fflush()
           fgetc()
           fgetpos()
           fgets()
           fgetwc()
           fgetws()
           fmtmsg()
           fopen()
           fpathconf()
           fprintf()
           fputc()
           fputs()
           fputwc()
           fputws()
           fread()
           freopen()
           fscanf()
           fseek()
           fseeko()
           fsetpos()
           fstat()
           fstatat() [Added in POSIX.1-2008]
           ftell()
           ftello()
           ftw()
           futimens() [Added in POSIX.1-2008]
           fwprintf()
           fwrite()
           fwscanf()
           getaddrinfo()
           getc()
           getc_unlocked()
           getchar()
           getchar_unlocked()
           getcwd()
           getdate()
           getdelim() [Added in POSIX.1-2008]
           getgrent()
           getgrgid()
           getgrgid_r()
           getgrnam()
           getgrnam_r()
           gethostbyaddr() [SUSv3 only (function removed in POSIX.1-2008)]
           gethostbyname() [SUSv3 only (function removed in POSIX.1-2008)]
           gethostent()
           gethostid()
           gethostname()
           getline() [Added in POSIX.1-2008]
           getlogin()
           getlogin_r()
           getnameinfo()
           getnetbyaddr()
           getnetbyname()
           getnetent()
           getopt() (if opterr is nonzero)
           getprotobyname()
           getprotobynumber()
           getprotoent()
           getpwent()
           getpwnam()
           getpwnam_r()
           getpwuid()
           getpwuid_r()
           gets()
           getservbyname()
           getservbyport()
           getservent()
           getutxent()
           getutxid()
           getutxline()
           getwc()
           getwchar()
           getwd() [SUSv3 only (function removed in POSIX.1-2008)]
           glob()
           iconv_close()
           iconv_open()
           ioctl()
           link()
           linkat() [Added in POSIX.1-2008]
           lio_listio() [Added in POSIX.1-2008]
           localtime()
           localtime_r()
           lockf() [Added in POSIX.1-2008]
           lseek()
           lstat()
           mkdir() [Added in POSIX.1-2008]
           mkdirat() [Added in POSIX.1-2008]
           mkdtemp() [Added in POSIX.1-2008]
           mkfifo() [Added in POSIX.1-2008]
           mkfifoat() [Added in POSIX.1-2008]
           mknod() [Added in POSIX.1-2008]
           mknodat() [Added in POSIX.1-2008]
           mkstemp()
           mktime()
           nftw()
           opendir()
           openlog()
           pathconf()
           pclose()
           perror()
           popen()
           posix_fadvise()
           posix_fallocate()
           posix_madvise()
           posix_openpt()
           posix_spawn()
           posix_spawnp()
           posix_trace_clear()
           posix_trace_close()
           posix_trace_create()
           posix_trace_create_withlog()
           posix_trace_eventtypelist_getnext_id()
           posix_trace_eventtypelist_rewind()
           posix_trace_flush()
           posix_trace_get_attr()
           posix_trace_get_filter()
           posix_trace_get_status()
           posix_trace_getnext_event()
           posix_trace_open()
           posix_trace_rewind()
           posix_trace_set_filter()
           posix_trace_shutdown()
           posix_trace_timedgetnext_event()
           posix_typed_mem_open()
           printf()
           psiginfo() [Added in POSIX.1-2008]
           psignal() [Added in POSIX.1-2008]
           pthread_rwlock_rdlock()
           pthread_rwlock_timedrdlock()
           pthread_rwlock_timedwrlock()
           pthread_rwlock_wrlock()
           putc()
           putc_unlocked()
           putchar()
           putchar_unlocked()
           puts()
           pututxline()
           putwc()
           putwchar()
           readdir()
           readdir_r()
           readlink() [Added in POSIX.1-2008]
           readlinkat() [Added in POSIX.1-2008]
           remove()
           rename()
           renameat() [Added in POSIX.1-2008]
           rewind()
           rewinddir()
           scandir() [Added in POSIX.1-2008]
           scanf()
           seekdir()
           semop()
           setgrent()
           sethostent()
           setnetent()
           setprotoent()
           setpwent()
           setservent()
           setutxent()
           sigpause() [Added in POSIX.1-2008]
           stat()
           strerror()
           strerror_r()
           strftime()
           symlink()
           symlinkat() [Added in POSIX.1-2008]
           sync()
           syslog()
           tmpfile()
           tmpnam()
           ttyname()
           ttyname_r()
           tzset()
           ungetc()
           ungetwc()
           unlink()
           unlinkat() [Added in POSIX.1-2008]
           utime() [Added in POSIX.1-2008]
           utimensat() [Added in POSIX.1-2008]
           utimes() [Added in POSIX.1-2008]
           vdprintf() [Added in POSIX.1-2008]
           vfprintf()
           vfwprintf()
           vprintf()
           vwprintf()
           wcsftime()
           wordexp()
           wprintf()
           wscanf()

       An implementation may also mark other functions not specified in the
       standard as cancellation points.  In particular, an implementation is
       likely to mark any nonstandard function that may block as a cancella‐
       tion point.  (This includes most functions that can touch files.)

 

这两个属性可分别通过pthread_setcancelstate和pthread_setcanceltype函数设置。

 

 

int oldstate;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);

printf("pre cancel state %d\n", oldstate); 

如果设置为PTHREAD_CANCEL_ENABLE,表示enabled,如果设置为PTHREAD_CANCEL_DISABLE,表示disabled。

 

 

int oldtype;
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);

printf("pre cancel type %d\n", oldtype); 

如果设置为PTHREAD_CANCEL_DEFERRED,表示deferred ,如果设置为PTHREAD_CANCEL_ASYNCHRONOUS,表示asynchronous

 

线程的这两个控制属性(可撤销属性):state、type默认分别为:PTHREAD_CANCEL_ENABLE、PTHREAD_CANCEL_DEFERRED。

 

 

 

第一种情况:通过pthread_exit函数终止线程执行。

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
 
 
int *exitStatus = NULL;
int count = 0;
 
void* run_up(void *arg)
{
printf("&exitStatus: %d, exitStatus: %d, *exitStatus: %d, &*exitStatus: %d\n", &exitStatus, exitStatus, *exitStatus, &*exitStatus);
 
pthread_exit((void *) exitStatus);
//pthread_exit(NULL);
//const char *s = "Thread funtion finished!";
//pthread_exit((void *) s);
 
//return (void *) exitStatus;
//return NULL;
//return (void *) "Thread funtion finished!";
}
 
 
int main()
{
 
exitStatus = (int *) malloc(sizeof(int));
*exitStatus = 8;
 
pthread_t pthread1, pthread2;
pthread_create(&pthread1, 
NULL, 
run_up, 
(void *) 0);
 
int *p_status = (int *) malloc(sizeof(int));
int **status = &p_status;
pthread_join(pthread1, (void **) status);
printf("&p_status: %d, p_status: %d, *p_status: %d, &*p_status: %d\n", &p_status, p_status, *p_status, &*p_status);
printf("&status: %d, status: %d, *status: %d, &*status: %d, **status: %d, &**status: %d\n", &status, status, *status, &*status, **status, &**status);
 
printf("final count: %d\n", count);
return 0;
}

 

 

include_pthread = /cygdrive/d/usr/lib/pthreads-w32-2-8-0-release/Pre-built.2/include
lib_pthread = /cygdrive/d/usr/lib/pthreads-w32-2-8-0-release/Pre-built.2/lib

OUTPUT = ../Debug/
objs = $(OUTPUT)*.o

make: make-std

make-nostd: clean
	g++ -c pthread_exit_test2.cpp -o ../Debug/pthread_exit_test2.o

	# -L$(lib_pthread) 指定lib库文件的位置:-L/cygdrive/d/usr/lib/pthreads-w32-2-8-0-release/Pre-built.2/lib
	# 如果没有指定lib库文件的位置:-L$(lib_pthread),需要将lib库文件拷贝到可以查找到的目录下。我这里是cygwin
	# 环境: $ cp -rf /cygdrive/d/usr/lib/pthreads-w32-2-8-0-release/Pre-built.2/lib/libpthreadGC2.a  /cygdrive/d/sbin/lib
	# 这样就可以不指定lib库文件的位置:
	# g++ $(objs) -o $(OUTPUT)pthread_exit_test2 -lpthreadGC2
	g++ $(objs) -o $(OUTPUT)pthread_exit_test2 -L$(lib_pthread) -lpthreadGC2

#	g++ $(objs) -o $(OUTPUT)pthread_exit_test2 -lpthreadGC2

make-std: clean
	g++ -c pthread_exit_test2.cpp -o ../Debug/pthread_exit_test2.o
	g++ $(objs) -o $(OUTPUT)pthread_exit_test2 -lpthread

clean:
	rm -Rf ./*.bak
	rm -Rf ./*.o
	rm -Rf ./*.exe
	rm -Rf ../Debug/*

 

pthread使用的是win版本:pthreads-w32-2-8-0-release,Pre-built中包含支持linux的pthread库:libpthreadGC2.a、libpthreadGCE2.a

将Pre-built中包含支持linux的pthread库拷贝到cygwin下:/cygdrive/d/sbin/lib

$ cp -rf /cygdrive/d/usr/lib/pthreads-w32-2-8-0-release/Pre-built.2/lib/libpthreadGC2.a  /cygdrive/d/sbin/lib/

make

$ make -f pthread_exit_test2.Makefile make

运行

$ ./pthread_exit_test2.exe

运行结果

&exitStatus: 4210712, exitStatus: 536937064, *exitStatus: 8, &*exitStatus: 536937064
&p_status: 2272376, p_status: 536937064, *p_status: 8, &*p_status: 536937064
&status: 2272372, status: 2272376, *status: 536937064, &*status: 2272376, **status: 8, &**status: 536937064

这个结果是正常的。

 

第二种情况:线程函数返回。

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>


int *exitStatus = NULL;

void* run_up(void *arg)
{
	printf("&exitStatus: %d, exitStatus: %d, *exitStatus: %d, &*exitStatus: %d\n", &exitStatus, exitStatus, *exitStatus, &*exitStatus);
	
	
	return (void *) exitStatus;
	//return NULL;
	//return (void *) "Thread funtion finished!";
}


int main()
{
	
	exitStatus = (int *) malloc(sizeof(int));
	*exitStatus = 8;

	pthread_t pthread1, pthread2;
	pthread_create(&pthread1, 
		NULL, 
		run_up, 
		(void *) 0);

	int *p_status = (int *) malloc(sizeof(int));
	int **status = &p_status;
	pthread_join(pthread1, (void **) status);
	printf("&p_status: %d, p_status: %d, *p_status: %d, &*p_status: %d\n", &p_status, p_status, *p_status, &*p_status);
	printf("&status: %d, status: %d, *status: %d, &*status: %d, **status: %d, &**status: %d\n", &status, status, *status, &*status, **status, &**status);

	return 0;
}

第三种情况:通过pthread_cancel函数取消线程执行。

 

线程的可撤销type属性为deferred的例子: 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <sys/types.h>
 
int *exitStatus = NULL;
 
void* run_up(void *arg)
{
  pid_t pid = getpid();
  // pid_t tid = gettid(); // Glibc does not provide a wrapper for this system call; call it using syscall(2).
  pthread_t tid = pthread_self();

  int i = 0;
  //int oldstate;
  //int oldtype;

  printf("pid: %ld, tid: %ld\n", pid, tid);
  printf("&exitStatus: %d, exitStatus: %d, *exitStatus: %d, &*exitStatus: %d\n", &exitStatus, exitStatus, *exitStatus, &*exitStatus);
 
  //pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
  //printf("pre cancel state %d\n", oldstate); 

  //pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
  //printf("pre cancel type %d\n", oldtype); 

  while(1)
  {
    i++;
	sleep(1);
	pthread_testcancel();
  }

  return (void *) exitStatus;
}
 
 
int main()
{
  exitStatus = (int *) malloc(sizeof(int));
  *exitStatus = 8;
 
  pthread_t pthread1, pthread2;
  int result = pthread_create(&pthread1, 
    NULL, 
    run_up, 
    (void *) 0);
  if (result)
  {
    printf("error: pthread_create, errno: %d\n", result);
  }
  printf("pthread: %ld\n", pthread1);
  
  printf("try to cancel thread %ld\n", pthread1); 
  
  pthread_cancel(pthread1);
 
  int *p_status = (int *) malloc(sizeof(int));
  int **status = &p_status;
  
  result = pthread_join(pthread1, (void **) status);
  if (result) 
  {
    printf("error: pthread_join, errno: %d\n", result);
  }
  if (p_status == NULL)
  {
	printf("p_status is null\n");
  } 
  else if (p_status == PTHREAD_CANCELED)
  {
	printf("thread %ld canceled.\n", pthread1);
  } 
  else 
  {
    printf("&p_status: %d, p_status: %d, *p_status: %d, &*p_status: %d\n", &p_status, p_status, *p_status, &*p_status);
	printf("&status: %d, status: %d, *status: %d, &*status: %d, **status: %d, &**status: %d\n", &status, status, *status, &*status, **status, &**status);
	
  }

  return 0;
}

这里要注意的是,线程执行函数run_up中while循环中的sleep(1);pthread_testcancel();函数调用,这两个都是可以作为取消点(Cancellation points)的函数。有这种函数调用,在向线程发起cancel请求后,线程在收到该请求后会延迟到这种函数调用的时候取消线程执行。

 

线程的可撤销type属性为asynchronous的例子: 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <sys/types.h>
 
int *exitStatus = NULL;
 
void* run_up(void *arg)
{
  pid_t pid = getpid();
  // pid_t tid = gettid(); // Glibc does not provide a wrapper for this system call; call it using syscall(2).
  pthread_t tid = pthread_self();

  int i = 0;
  //int oldstate;
  int oldtype;

  printf("pid: %ld, tid: %ld\n", pid, tid);
  printf("&exitStatus: %d, exitStatus: %d, *exitStatus: %d, &*exitStatus: %d\n", &exitStatus, exitStatus, *exitStatus, &*exitStatus);
 
  //pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
  //printf("pre cancel state %d\n", oldstate); 

  //pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
  //printf("pre cancel type %d\n", oldtype); 

  pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);
  printf("pre cancel type %d\n", oldtype); 

  while(1)
  {
    i++;
  }

  return (void *) exitStatus;
}
 
 
int main()
{
  exitStatus = (int *) malloc(sizeof(int));
  *exitStatus = 8;
 
  pthread_t pthread1, pthread2;
  int result = pthread_create(&pthread1, 
    NULL, 
    run_up, 
    (void *) 0);
  if (result)
  {
    printf("error: pthread_create, errno: %d\n", result);
  }
  printf("pthread: %ld\n", pthread1);
  
  printf("try to cancel thread %ld\n", pthread1); 
  
  pthread_cancel(pthread1);
 
  int *p_status = (int *) malloc(sizeof(int));
  int **status = &p_status;
  
  result = pthread_join(pthread1, (void **) status);
  if (result) 
  {
    printf("error: pthread_join, errno: %d\n", result);
  }
  if (p_status == NULL)
  {
	printf("p_status is null\n");
  } 
  else if (p_status == PTHREAD_CANCELED)
  {
	printf("thread %ld canceled.\n", pthread1);
  } 
  else 
  {
    printf("&p_status: %d, p_status: %d, *p_status: %d, &*p_status: %d\n", &p_status, p_status, *p_status, &*p_status);
	printf("&status: %d, status: %d, *status: %d, &*status: %d, **status: %d, &**status: %d\n", &status, status, *status, &*status, **status, &**status);
	
  }

  return 0;
}

这里要注意的是,线程执行函数run_up中while循环中并没有sleep(1);pthread_testcancel();这样的函数调用(可以作为取消点(Cancellation points)的函数)。通常被取消线程是可以取消执行的,但有时候最后也不能保证线程取消成功。

 

为了保证线程最后可以被取消成功,可以像上面的例子那样尝试调用一个可以作为取消点(Cancellation points)的函数。如像上面那样在循环里边调用sleep(1);pthread_testcancel();

 

或者在发起cancel请求的时候多尝试几次:

pthread_cancel(pthread1);
int tryt = 0;
while (tryt < 100)
{
  pthread_cancel(pthread1);
  sleep(0.1);
  tryt++;
}

 

 

pthread w32版本线程终止(pthread_exit)后通过pthread_join获取线程终止返回值的问题

 

pthread w32版本线程终止(pthread_exit)后的返回值的问题。

通过pthread_exit终止线程的执行,pthread_join等待线程终止返回的值在pthread w32版本和标准pthread不一样。

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
 
 
int *exitStatus = NULL;
int count = 0;
 
void* run_up(void *arg)
{
printf("&exitStatus: %d, exitStatus: %d, *exitStatus: %d, &*exitStatus: %d\n", &exitStatus, exitStatus, *exitStatus, &*exitStatus);
 
pthread_exit((void *) exitStatus);
//pthread_exit(NULL);
//const char *s = "Thread funtion finished!";
//pthread_exit((void *) s);
 
//return (void *) exitStatus;
//return NULL;
//return (void *) "Thread funtion finished!";
}
 
 
int main()
{
 
exitStatus = (int *) malloc(sizeof(int));
*exitStatus = 8;
 
pthread_t pthread1, pthread2;
pthread_create(&pthread1, 
NULL, 
run_up, 
(void *) 0);
 
int *p_status = (int *) malloc(sizeof(int));
int **status = &p_status;
pthread_join(pthread1, (void **) status);
printf("&p_status: %d, p_status: %d, *p_status: %d, &*p_status: %d\n", &p_status, p_status, *p_status, &*p_status);
printf("&status: %d, status: %d, *status: %d, &*status: %d, **status: %d, &**status: %d\n", &status, status, *status, &*status, **status, &**status);
 
printf("final count: %d\n", count);
return 0;
}

 

 

include_pthread = /cygdrive/d/usr/lib/pthreads-w32-2-8-0-release/Pre-built.2/include
lib_pthread = /cygdrive/d/usr/lib/pthreads-w32-2-8-0-release/Pre-built.2/lib

OUTPUT = ../Debug/
objs = $(OUTPUT)*.o

make: make-std

make-nostd: clean
	g++ -c pthread_exit_test2.cpp -o ../Debug/pthread_exit_test2.o

	# -L$(lib_pthread) 指定lib库文件的位置:-L/cygdrive/d/usr/lib/pthreads-w32-2-8-0-release/Pre-built.2/lib
	# 如果没有指定lib库文件的位置:-L$(lib_pthread),需要将lib库文件拷贝到可以查找到的目录下。我这里是cygwin
	# 环境: $ cp -rf /cygdrive/d/usr/lib/pthreads-w32-2-8-0-release/Pre-built.2/lib/libpthreadGC2.a  /cygdrive/d/sbin/lib
	# 这样就可以不指定lib库文件的位置:
	# g++ $(objs) -o $(OUTPUT)pthread_exit_test2 -lpthreadGC2
	g++ $(objs) -o $(OUTPUT)pthread_exit_test2 -L$(lib_pthread) -lpthreadGC2

#	g++ $(objs) -o $(OUTPUT)pthread_exit_test2 -lpthreadGC2

make-std: clean
	g++ -c pthread_exit_test2.cpp -o ../Debug/pthread_exit_test2.o
	g++ $(objs) -o $(OUTPUT)pthread_exit_test2 -lpthread

clean:
	rm -Rf ./*.bak
	rm -Rf ./*.o
	rm -Rf ./*.exe
	rm -Rf ../Debug/*

 

pthread使用的是win版本:pthreads-w32-2-8-0-release,Pre-built中包含支持linux的pthread库:libpthreadGC2.a、libpthreadGCE2.a

将Pre-built中包含支持linux的pthread库拷贝到cygwin下:/cygdrive/d/sbin/lib

$ cp -rf /cygdrive/d/usr/lib/pthreads-w32-2-8-0-release/Pre-built.2/lib/libpthreadGC2.a  /cygdrive/d/sbin/lib/

make

$ make -f pthread_exit_test2.Makefile make

运行

$ ./pthread_exit_test2.exe

运行结果

&exitStatus: 4210712, exitStatus: 536937064, *exitStatus: 8, &*exitStatus: 536937064
&p_status: 2272376, p_status: 536937064, *p_status: 8, &*p_status: 536937064
&status: 2272372, status: 2272376, *status: 536937064, &*status: 2272376, **status: 8, &**status: 536937064

这个结果是正常的。

但奇怪的是,如果pthread使用的是win版本:pthreads-w32-2-8-0-release

make

$ make -f pthread_exit_test2.Makefile make-nostd

运行

 

$ ./pthread_exit_test2.exe

运行结果

 

运行输出:

&p_status: 2272376, p_status: 536937080, *p_status: 0, &*p_status: 536937080
&exitStatus: 4210712, exitStatus: 536937064, *exitStatus: 8, &*exitStatus: 536937064
&status: 2272372, status: 2272376, *status: 536937080, &*status: 2272376, **status: 0, &**status: 536937080

这个结果就不正常了,和预期结果不一致。

有时候还没打印出exitStatus的值:

&p_status: 2272376, p_status: 536937080, *p_status: 0, &*p_status: 536937080
&status: 2272372, status: 2272376, *status: 536937080, &*status: 2272376, **status: 0, &**status: 536937080

这个就奇怪了。

 

 

这个问题后来看了下,发现pthread_join函数调用失败,返回错误:error: errno: 3

根据errno.h中的定义:

#define  ESRCH 3         /* No such process */

 

No thread with the ID thread could be found.

 

意思是没找到这个线程?

 

所以这里pthread_join调用后并没有获取到线程终止后的返回值,并没有将线程终止后的返回值拷贝到pthread_join的第二个参数。

 

只是我在线程函数中可以打印出线程id, 而且和printf("pthread: %ld\n", pthread1);打印出来的值是一致的,而且线程函数也执行了,怎么就找不到这个线程呢?

 

pid_t pid = getpid();
// pid_t tid = gettid(); // Glibc does not provide a wrapper for this system call; call it using syscall(2).
pthread_t tid = pthread_self();
printf("pid: %ld, tid: %ld\n", pid, tid);

 

那么这个pthread_join调用失败具体是什么原因?

 

 

 

 

 

关于线程执行返回值,线程执行终止返回有4中方式,可以通过pthread_join函数得到线程终止时的返回值,前提是对应的线程是joinable的(线程可以是joinable或者detached的)。参考文章:https://lobin.iteye.com/blog/2040285

 

论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics