用dede做网站去掉版权/微信营销的方法有哪些
概念
线程连接 thread join
线程分离 thread detach
单词 | 音标 | 释义 |
---|---|---|
join | /dʒɔɪn/ | v. 成为……的一员,参加;连接,接合;联合,汇合;上(火车、飞机等);和……作伴,随同 n. 连接处,接合点 |
detach | /dɪˈtætʃ/ | v. 拆下,使分离;脱离,摆脱;派遣,分派 |
线程join的由来
与Linux类似,SylixOS下每运行一个app程序,其实就是启动了一个进程,它从main函数开始,待函数退出或exit,进程结束并释放内核资源。操作系统下,CPU都是通过执行中断或线程来运行的,所以main函数其实是在进程自动创建的主线程序执行的。如果出现比较复杂,需要手动创建多个线程来完成功能,这些后创建的线程都是进程的子线程。如果主线程在其他子线程还没有运行完毕的情况下退出,也会使得进程退出,而进程退出会关闭它的所有子线程并释放内核资源,这就会造成运行逻辑错误。为解决这个问题我们就需要主线程等待各子线程运行完毕后再退出,这里就要用到线程连接函数join,使用该函数还能获取目标线程运行完毕的返回值。
线程有两种状态可连接状态(joinable)和不可连接状态(unjoinable,也就是分离状态detach)。如果线程是joinable状态,当线程函数自己退出时或exit时都不会释放线程所占内核资源(堆栈和线程描述符等),只有当调用了join方法后这些资源才会被释放。若是unjoinable状态的线程,这些资源在线程函数退出时或exit时自动会被释放。
线程默认是可连接的,在线程创建时通过增加属性LW_OPTION_THREAD_DETACHED
可指定为不可连接,也可以在运行中调用detach方法设置为不可连接,但一个不可连接函数无法再设置为可连接状态。一个已被join的线程,在运行中调用detach方法,会使得join立即退出。
线程连接有时也叫线程合并,因为俩独立线程本身可以并行或交替执行,但在调用join后,就使得执行完一个线程才能顺序执行另一个线程,如同把两个线程合并成一个线程一样。
线程join的用途
使用的线程连接的场景:
- 防止主线程提前退出导致进程关闭,保证各子线程正常运行。
- 等待目标线程结束,执行逻辑上需要等待某线程先执行完毕才能进行后续动作。
- 获取目标线程的返回值。
- 及早释放目标线程执行完毕后占用的系统资源。
对于用途3有一种特殊的应用情景,当主流程调用某复杂过程时,可以通过函数来直接调用,也可通过创建线程并join来间接调用,这两种方法都是同步的,但效果会略有差别。线程间接调用可以使用独立的线程空间,可以根据需求设置堆栈大小,优先级等特性而不影响主线程,也可以让主线程屏蔽此过程中的错误处理,系统耦合等问题。
线程join流程
- A线程创建B线程。如果A线程优先级高,A线程继续执行;如果B线程优先级高,则B线程抢占A线程先运行;如果AB线程优先级相同这交替执行。
- A线程连接B线程,A线程被阻塞,直到B线程结束。
- 内核获知B线程结束且被A线程连接,则释放B线程内核资源,并将B线程返回值传递给A的join方法。
- A线程被唤醒的,并得到B线程返回值,A线程进行运行。
注意:
- 只有可连接的线程才能被join。
- 线程只能join本进程的线程。
- 线程不能自己join自己。
- 线程被多处join,则最先执行的join函数会等待线程并得到返回值,其他直接返回错误。
接口
SylixOS原生接口
/*********************************************************************************************************
** 函数名称: API_ThreadJoin
** 功能描述: 线程合并(不得在中断中调用)
** 输 入 :
** ulId 要合并的目的线程句柄
** ppvRetValAddr 存放线程返回值的地址
** 输 出 : ID
*********************************************************************************************************/
ULONG API_ThreadJoin (LW_OBJECT_HANDLE ulId, PVOID *ppvRetValAddr)
ulId 参数用于指定连接的目标线程;ppvRetValAddr参数用于接收目标线程的返回值,如果目标线程没有返回值,或者不需要线程的返回值,可以将 ppvRetValAddr 参数置为 NULL。
如果join函数执行成功,会一直阻塞调用它的线程,直至目标线程执行结束,返回值为 0;如果执行失败,函数会立即退出,根据失败原因返回相应的非零值,例如:
- ERROR_THREAD_NULL:线程无效,或非本进程线程。
- ERROR_THREAD_JOIN_SELF:线程自己不能被自己连接。
- ERROR_THREAD_DETACHED:目标线程为分离状态不允许连接。
/*********************************************************************************************************
** 函数名称: API_ThreadDetach
** 功能描述: 禁止其他线程合并指定线程(不得在中断中调用)
** 输 入 :
** ulId 线程句柄
** 输 出 : ID
*********************************************************************************************************/
ULONG API_ThreadDetach (LW_OBJECT_HANDLE ulId)
/*********************************************************************************************************
** 函数名称: API_ThreadDetachEx
** 功能描述: 禁止其他线程合并指定线程(不得在中断中调用)
** 输 入 :
** ulId 线程句柄
** pvRetVal 返回值
** 输 出 : ID
*********************************************************************************************************/
ULONG API_ThreadDetachEx (LW_OBJECT_HANDLE ulId, PVOID pvRetVal)
系统同时支持POSIX标准对应接口,和原生接口用法是一样,而其实现其实是间接调用的原生接口。
int pthread_join(pthread_t thread, void **ppstatus);
int pthread_detach(pthread_t thread);
注意,pthread_join接口在Linux和SylixOS下调用有少许差别。如果目标线程在pthread_join等待之前结束,在SylixOS下返回无效线程错误值,而在Linux下会正常返回。原因是SylixOS实现中会先检测线程参数是否有效,如果线程无效则直接返回线程无效的错误。在程序移植和编程时要加一注意。
用法
#include <stdio.h>static VOID *thread1 (VOID *pvArg)
{char *str = "thread1 return";printf("thread1 run\n");return (str);
}static VOID *thread2 (VOID *pvArg)
{char *str = "thread2 return";int i;for (i = 0; i < 3; i++) {printf("thread2 run %d\n", i);API_ThreadExit("thread2 exit");sleep(1);}return (str);
}static VOID *thread3 (VOID *pvArg)
{char *str = "thread3 return";printf("thread3 run %d\n", 0);sleep(1);API_ThreadDetachEx(API_ThreadIdSelf(), "thread3 Detach");printf("thread3 run %d\n", 1);sleep(1);printf("thread3 run %d\n", 2);sleep(1);return (str);
}int main (int argc, char **argv)
{LW_CLASS_THREADATTR hThreadAttr = API_ThreadAttrGetDefault();LW_OBJECT_HANDLE ulId;CHAR *str;INT iRes;hThreadAttr.THREADATTR_ulOption |= LW_OPTION_THREAD_DETACHED;API_ThreadCreate("thread1",(PTHREAD_START_ROUTINE)thread1,&hThreadAttr,&ulId);iRes = API_ThreadJoin(ulId, (PVOID *)&str);printf("thread1 join. iRes = %d, rtn = %s\n", iRes, str);hThreadAttr = API_ThreadAttrGetDefault();API_ThreadCreate("thread1",(PTHREAD_START_ROUTINE)thread1,&hThreadAttr,&ulId);iRes = API_ThreadJoin(ulId, (PVOID *)&str);printf("thread1 join. iRes = %d, rtn = %s\n", iRes, str);iRes = API_ThreadJoin(ulId, (PVOID *)&str);printf("thread1 join. iRes = %d, rtn = %s\n", iRes, str);API_ThreadCreate("thread1",(PTHREAD_START_ROUTINE)thread1,&hThreadAttr,&ulId);sleep(1);iRes = API_ThreadJoin(ulId, (PVOID *)&str);printf("thread1 join. iRes = %d, rtn = %s\n", iRes, str);API_ThreadCreate("thread2",(PTHREAD_START_ROUTINE)thread2,&hThreadAttr,&ulId);iRes = API_ThreadJoin(ulId, (PVOID *)&str);printf("thread2 join. iRes = %d, rtn = %s\n", iRes, str);API_ThreadCreate("thread3",(PTHREAD_START_ROUTINE)thread3,&hThreadAttr,&ulId);iRes = API_ThreadJoin(ulId, (PVOID *)&str);printf("thread3 join. iRes = %d, rtn = %s\n", iRes, str);sleep(3);return (0);
}
[root@sylixos:/root]#
[root@sylixos:/root]# /apps/sylixosAppExample/srcSylixos/threadJoin
thread1 join. iRes = 518, rtn = p5`
thread1 run
thread1 run
thread1 join. iRes = 0, rtn = thread1 return
thread1 join. iRes = 507, rtn = thread1 return
thread1 run
thread1 join. iRes = 507, rtn = thread1 return
thread2 run 0
thread2 join. iRes = 0, rtn = thread2 exit
thread3 run 0
thread3 run 1
thread3 join. iRes = 0, rtn = thread3 Detach
thread3 run 2
[root@sylixos:/root]#