C - pthread & fork 多線程

使用多CPU,同時執行另一個行程的方法有 pthread、fork


pthread

跟主行程共用同一組全域變數,也與主行程的 pid 相同(編譯時需要加上 -pthread)

定義


#include <pthread.h>
int pthread_create(pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void *),
void *restrict arg); // 建立 thread
int pthread_join(pthread_t thread, void **retval); // 等待指定的 thread ID 結束,並接收回傳值
pthread_t pthread_self(void); // 回傳目前的 thread ID
void pthread_exit(void *retval);// 在主行程執行,會等待所有 thread 結束後,主行程才結束 (參數傳入 NULL 即可)
// 在子行程執行,結束這個 thread,並傳回值


#include <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);



 基本用法


void *func(void *param) {
pthread_t tid = pthread_self();
pthread_exit("returnValue");
return NULLL;
}
int main(int argc, char **argv) {
char *param = "args";
char *retVal = NULL;
pthread_t tid;
pthread_create(&tid, NULL, (void *)func, param);
pthread_join(&tid, &retVal);
pthread_exit(NULL);
return 0;
}



取得pid

getpid()                        // 會取到跟主行程一樣的pid
pthread_self()              // 會取到 thread id 而不是 pid
gettid()                        // 我的機器無法使用
syscall(SYS_gettid)    // 最後用這個成功了 (要 #include <sys/syscall.h>)
syscall(__NR_gettid)  // 這個也可以


fork

會完整複製一份目前的所有記憶體,所以就算跟主行程更改到相同的變數名稱,也不會互相影響。並與主行程的 pid 不同。

定義


#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);



基本用法 


pid_t ret;
if(!(ret = fork())) {
// 呼叫成功,這裡已經是另一個子行程了
} else if(ret < 0) {
// 呼叫失敗
} else if(ret > 0) {
// 呼叫成功,這裡為主行程(父行程),ret為子行程的pid
}


switch (fork()) {
case -1: /* Something went wrong */
exit(-1);
case 0: /* Child: close off stdout, stdin and stderr */
close(0);
close(1); /* stdout */
close(2);
setsid();
_main();
break;
default: /* Parent: Just exit */
exit(0);
}


注意事項

如果父行程比子行程先結束,子行程狀態會被改為 Z (Zombie)
使用 wait(等待子行程結束) 或 signal(處理 SIGCHLD) 可解決此情況


static void sig_handler(int sig) {
int retval;
if (sig == SIGCHLD){
// 等待子行程的結束狀態
wait(&retval);
printf("CATCH SIGNAL PID=%d\n",getpid());
}
}
int main(int argc, char **argv) {
// 設定 signal 接收 SIGCHLD 信號
signal(SIGCHLD, sig_handler);
pid_t ret;
if(!(ret = fork())) {
// 呼叫成功,這裡已經是另一個子行程了
}
}






(1). htop 按 t (顯示進程線程嵌套關係)和 H (顯示線程) ,然後 F4 過濾進程名。
(2). ps -eLf | grep java (快照,帶線程命令,e 是顯示全部進程,L 是顯示線程,f 全格式輸出) 
(3). pstree -p <pid> (顯示進程樹,不加 pid 顯示所有) 
(4). top -Hp <pid> (實時) 
(5). ps -T -p <pid> (快照)



沒有留言:

張貼留言