thread base function

    Just as a process has a process ID, each thread also has a thread ID. However, process IDs are unique across the system, and thread IDs are only meaningful within the context of the process to which they belong. Thread IDs are represented using the data type pthread_t, and since this type may be different in different implementations (some integers, some structs), a comparison function pthread_equal is provided for portability. In addition, the pthread_self function is also provided to obtain its own thread ID.
#include <pthread.h>
int pthread_equal(pthread_t tid1, pthread_t tid2);
                                      /* Return value: if equal, return non-zero; otherwise, return 0 */
pthread_t pthread_self(void); /* Return value: thread ID of the calling thread */

    Instead, to create a thread, use the pthread_create function.
#include <pthread.h>
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr,
                   void* (*start_rtm)(void *), void *restrict arg);
                                 /* Return value: if successful, return 0; otherwise, return error number */

    When the function returns successfully, the newly created thread ID will be stored in the memory pointed to by tidp. The attr parameter is used to customize different thread attributes. When it is NULL, it means to use the default attributes. The newly created thread starts running at the address of the start_rtm function, and the required parameters are placed at the address pointed to by the untyped pointer parameter arg. If there are multiple parameters, you can put them into a structure, and then pass the address of the structure as the arg parameter (but make sure that the memory used by the structure is still valid after the caller completes the call, otherwise the value of the structure may be implicitly changed by other functions using the same stack memory).
    When a thread is created there is no guarantee that the newly created thread or the calling thread will run first. A newly created thread has access to the process' address space and inherits the calling thread's floating point context and signal mask, but the thread's pending signal set is cleared.
    Note that pthread-like functions usually return an error code when the call fails, rather than directly modifying the global variable errno, in order to limit the scope of the error to the function that caused the error.
    If any thread in the process calls exit, _exit, or _Exit, or receives a signal whose default action is to terminate the process, the entire process will be terminated. To make a single thread stop its control flow without terminating the entire process, there are 3 ways.
    (1) Return from the startup routine, the return value is the thread's exit code.
    (2) Cancelled by other threads in the same process.
    (3) Call the pthread_exit function.
#include <pthread.h>
void pthread_exit(void *rval_ptr);
int pthread_join(pthread_t tid, void **rval_ptr);
int pthread_detach(pthread_t tid);
                        /* The return values ​​of the two functions: if successful, return 0; otherwise, return the error code */

    The untyped pointer parameter rval_ptr in pthread_exit is used like a single parameter passed to the startup routine. Other threads in the process can access this pointer by calling the pthread_join function. pthread_join causes the calling thread to block until the specified thread calls pthread_exit, returns from the startup routine, or is cancelled. If the thread simply returns from its startup routine, rval_ptr contains the return code; if the thread is canceled, the memory location specified by rval_ptr is set to PTHREAD_CANCELED. If you are not interested in the thread's return value, you can set rval_ptr to NULL, so that calling pthread_join only waits for the specified thread to terminate without getting the thread's termination status.
    The pthread_detach function can detach a thread. By default, a thread's termination state is preserved until pthread_join is called on that thread. If the thread has been detached, the thread's underlying storage resources can be reclaimed immediately when the thread terminates. Calling pthread_join after the thread has detached will yield undefined behavior.
    Although it is not portable to print thread IDs, a small test program can be written to accomplish this task. The program below prints the process ID, new thread ID, and initial thread ID.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

void printtid(const char *s){
	pid_t		pid = getpid();
	pthread_t	tid = pthread_self();
	printf("%s pid %lu, tid %lu (0x%lx)\n", s, (unsigned long)pid,
			(unsigned long)tid, (unsigned long)tid );
}

void *tfunc(void *arg){
	printtid("new thread:");
	pthread_exit((void *)1); // or return (void *)1;
}

int main(void){
	pthread_t	ntid;
	if(pthread_create(&ntid, NULL, tfunc, NULL) != 0){
		printf("can't create thread\n");
		exit(1);
	}
	printtid("main thread:");
	void *tret;
	if(pthread_join(ntid, &tret) != 0){
		printf("pthread_join error\n");
		exit(1);
	}
	printf("thread exit code: %ld\n", (long)tret);
	exit(0);
}

    Running the program on Linux gives:
$ ./print_tid.out
main thread: pid 29340, tid 140178125190912 (0x7f7dc35b7700)
new thread: pid 29340, tid 140178125182720 (0x7f7dc35b5700)
thread exit code: 1

    It can be seen that the threads are running in the same process, and the thread ID data type here is very similar to a pointer (however, the fact is that the pthread_t type on Linux is represented by an unsigned long integer).
    A thread can call the pthread_cannel function to request the cancellation of other threads in the same process (as if abort was called in the process), and can also use the pthread_cleanup_push function to arrange for thread cleanup handlers that need to be called when the thread exits (as the atexit function in the process), they are executed in the reverse order of registration.
#include <pthread.h>
int pthread_cancel(pthread_t tid);
                                 /* Return value: if successful, return 0; otherwise, return error number */
void pthread_cleanup_push(void (*rtn)(void *), void *arg);
void pthread_cleanup_pop(int execute);

    By default, the pthread_cancel function causes the thread tid to behave as if the pthread_exit function was called with the parameter PTHREAD_CANCELED, but the thread can choose to ignore cancellation or control how it is canceled. Note that this function does not wait for the thread to terminate, it just makes a request.
    When the thread performs the following actions, the pthread_cleanup_push function will call the cleanup function rtn with only one parameter arg:
    * when pthread_exit is called;
    * when the request is canceled accordingly;
    * when pthread_cleanup_pop is called with a non-zero execute parameter. If execute is 0, the cleanup function will not be called.
    In either case, pthread_cleanup_pop will remove the cleanup handler established by the last pthread_cleanup_push call. Note that pthread_cleanup_pop and pthread_cleanup_push must be used as matched pairs in the same scope as threads, since they may be implemented as macros.
    The following program gives an example of how to use a thread cleanup handler. Here to match pthread_cleanup_push, we call the pthread_cleanup_pop function with parameter 0.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

void cleanup(void *arg){
	printf("cleanup: %s\n", (char *)arg);
}

void *thr_fn1(void *arg){
	printf("thread 1 starts push\n");
	pthread_cleanup_push(cleanup, "first clean handler in thread 1");
	pthread_cleanup_push(cleanup, "second clean handler in thread 1");
	printf("thread 1 push complete\n");
	if(arg)
		return (void *)1;
	pthread_cleanup_pop(0);
	pthread_cleanup_pop(0);
	return (void *)1;
}

void *thr_fn2(void *arg){
	printf("thread 2 starts push\n");
	pthread_cleanup_push(cleanup, "first clean handler in thread 2");
	pthread_cleanup_push(cleanup, "second clean handler in thread 2");
	printf("thread 2 push complete\n");
	if(arg)
		pthread_exit((void *)2);
	pthread_cleanup_pop(0);
	pthread_cleanup_pop(0);
	pthread_exit((void *)2);
}

int main(void){
	pthread_t	tid1, tid2;
	void *rtn;
	if(pthread_create(&tid1, NULL, thr_fn1, (void *)1) != 0){
		printf("pthread_create 1 error\n");
		exit(1);
	}
	if(pthread_create(&tid2, NULL, thr_fn2, (void *)1) != 0){
		printf("pthread_create 2 error\n");
		exit(1);
	}
	if(pthread_join(tid1, &rtn) != 0){
		printf("pthread_join 1 error\n");
		exit(1);
	}
	printf("thread 1 return code: %ld\n", (long)rtn);
	if(pthread_join(tid2, &rtn) != 0){
		printf("pthread_join 2 error\n");
		exit(2);
	}
	printf("thread 2 exit code: %ld\n", (long)rtn);
	exit(0);
}

    Result on Linux:
$ ./threadClean.out
thread 2 starts push
thread 2 push complete
cleanup: second clean handler in thread 2
cleanup: first clean handler in thread 2
thread 1 starts push
thread 1 push complete
thread 1 return code: 1
thread 2 exit code: 2

    From the output it can be seen that only the cleanup handler of the second thread is called because the first thread is returned using return and the second is returned by calling pthread_exit. Also, cleanup handlers are called in the reverse order of installation.
    Running the program on FreeBSD or Mac OS will throw a segfault and generate a core file. Because pthread_cleanup_push on these two platforms is implemented with macros, and macros store some context on the stack. When thread 1 returns between the call to pthread_cleanup_push and the call to pthread_cleanup_pop, the stack has been overwritten, and both platforms use this overwritten context when calling the cleanup routine. The only portable way is to call pthread_exit.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326080839&siteId=291194637