设置主线程 linux

2020-05-29 11:35:01 lizhengze1117 阅读数 151

最常见的情况,主线程中开启了一个子线程,开启之后,主线程与子线程互不影响各自的生命周期,

即主线程结束,子线程还可以继续执行;子线程结束,主线程也能继续执行。
测试代码如下:

public class TestThread{
    public static void main(String[] args) throws InterruptedException {
        System.out.println("主线程启动。。。。");
        Thread thread = new Thread(new ChildThread());
        thread.start();
        System.out.println("主线程结束。。。。");
    }
}
class ChildThread implements Runnable{
    @Override
    public void run() {
        try {
            System.out.println("子线程启动。。。。");
            Thread.sleep(5000);
            System.out.println("子线程结束。。。。");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果如下:
在这里插入图片描述

主线程开启了子线程,但是主线程结束,子线程也随之结束

测试代码如下:

public class TestThread{
    public static void main(String[] args) throws InterruptedException {
        System.out.println("主线程启动。。。。");
        Thread thread = new Thread(new ChildThread());
        //thread.setDaemon(true);
        thread.start();
        System.out.println("主线程结束。。。。");
    }
}
class ChildThread implements Runnable{
    @Override
    public void run() {
        try {
            System.out.println("子线程启动。。。。");
            ThirdThread thiredThread = new ThirdThread();
            thiredThread.setDaemon(true);
            thiredThread.start();
            Thread.sleep(1000);
            System.out.println("子线程结束。。。。");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class ThirdThread extends Thread{
    @Override
    public void run() {
        System.out.println("孙子线程启动。。。。");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("孙子线程结束。。。。");
    }
}

运行结果:
在这里插入图片描述
注意:这里使用了Thread.setDaemon(true)方法把线程ThirdThread 设置成了线程ChildThread的守护线程,所以ChildThread线程一结束,ThirdThread 线程也就随之结束。
对于运行中的线程,不能设置为守护线程,不然会抛出java.lang.IllegalThreadStateException异常。

主线程开启了一个子线程,主线程必须要等子线程运行完之后,才能结束主线程

测试代码如下:

public class TestThread{
    public static void main(String[] args) throws InterruptedException {
        System.out.println("主线程启动。。。。");
        Thread thread = new Thread(new ChildThread());
        thread.start();
        thread.join();
        System.out.println("主线程结束。。。。");
    }
}
class ChildThread implements Runnable{
    @Override
    public void run() {
        try {
            System.out.println("子线程启动。。。。");
            Thread.sleep(5000);
            System.out.println("子线程结束。。。。");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述
注意:这里使用了join()方法,让主线程等待子线程结束,然后主线程继续执行。这里join()方法必须要在子线程启动之后,再调用。
进程是资源分配的基本单位,线程是cpu调度的基本单位。对于cpu来说,其实不存在主线程和子线程之分,都是一个线程。进程的资源是进程下面的线程所共享的,只要进程还在,线程就可以正常执行,也就是说线程是依赖于进程的,线程与线程之间并不存在依赖关系,一个线程的死亡理论上不会对其他线程造成影响。但是上面通过调用JVM提供的接口,例如setDaemonjoin改变了主线程与子线程的关系,这些应该是JVM接口代码做了处理干扰了线程的生命周期。
守护线程与非守护线程本质上没什么区别,但是如果虚拟机中存活的线程都是守护线程的时候,虚拟机就会退出,只要虚拟机中还有一个非守护线程,虚拟机就不会退出。
本次文章,借鉴了http://blog.csdn.net/aitangyong/article/details/16858273与http://blog.csdn.net/u013905744/article/details/73741056两篇前辈文章的内容。

转载自:https://blog.csdn.net/zhenwei1994/article/details/78779230

2013-07-16 11:53:55 ctthunagchneg 阅读数 1469

我们在一个线程中经常会创建另外的新线程,如果主线程退出,会不会影响它所创建的新线程呢?下面就来讨论一下。

1、  主线程等待新线程先结束退出,主线程后退出。正常执行。

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>

pthread_t ntid;//线程ID

void printids(const char *s)
{
        pid_t pid;
        pthread_t tid;
        pid = getpid();
        tid = pthread_self();
        printf("%s pid %u tid %u (0x%x)\n",s,(unsigned int)pid,
                        (unsigned int)tid,(unsigned int)tid);
}

void *thrfun(void *arg){
        //sleep(1);//使得主线程先退出
        printids("new thread");

        return ((void *)0);
}

int main(){
        int err;
        err = pthread_create(&ntid,NULL,thrfun,NULL);

        if(err != 0)
                perror("pthread_create");
        printids("main thread");

        sleep(1);//等待新线程先结束

        exit(0);
}
运行结果:

huangcheng@ubuntu:~$ ./a.out
main thread pid 2344 tid 3077813952 (0xb773b6c0)
new thread pid 2344 tid 3077811056 (0xb773ab70)

2、  进程先退出,新线程也会立即退出,系统清除所有资源。

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>

pthread_t ntid;//线程ID

void printids(const char *s)
{
        pid_t pid;
        pthread_t tid;
        pid = getpid();
        tid = pthread_self();
        printf("%s pid %u tid %u (0x%x)\n",s,(unsigned int)pid,
                        (unsigned int)tid,(unsigned int)tid);
}

void *thrfun(void *arg){
        sleep(1);//使得主线程先退出
        printids("new thread");

        return ((void *)0);
}

int main(){
        int err;
        err = pthread_create(&ntid,NULL,thrfun,NULL);

        if(err != 0)
                perror("pthread_create");
        printids("main thread");

        //sleep(1);//等待新线程先结束

        exit(0);
}
运行结果:

huangcheng@ubuntu:~$ ./a.out
main thread pid 2366 tid 3077641920 (0xb77116c0)
可以发现主线程退出后所创建的新线程也停止运行了。


3、如果主线程调用了pthread_exit,那么它退出了,子线程也不会退出。

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>

pthread_t ntid;//线程ID

void printids(const char *s)
{
        pid_t pid;
        pthread_t tid;
        pid = getpid();
        tid = pthread_self();
        printf("%s pid %u tid %u (0x%x)\n",s,(unsigned int)pid,
                        (unsigned int)tid,(unsigned int)tid);
}

void *thrfun(void *arg){
        sleep(1);//使得主线程先退出
        printids("new thread");

        return ((void *)0);
}

int main(){
        int err;
        err = pthread_create(&ntid,NULL,thrfun,NULL);

        if(err != 0)
                perror("pthread_create");
        printids("main thread");

        //sleep(1);//等待新线程先结束
		pthread_exit(NULL);

      //  exit(0);
}
运行结果:

huangcheng@ubuntu:~$ ./a.out
main thread pid 2385 tid 3077768896 (0xb77306c0)
new thread pid 2385 tid 3077766000 (0xb772fb70)

POSIX标准定义:

When you program with POSIX Threads API,there is one thing about pthread_exit() that you may ignore for mistake. Insubroutines that complete normally, there is nothing special you have to dounless you want to pass a return code back using pthread_exit(). The completionwon't affect the other threads which were created by the main thread of thissubroutine. However, in main(), when the code has been executed to the end,there could leave a choice for you. If you want to kill all the threads thatmain() created before, you can dispense with calling any functions. But if you want to keep the process and all the other threadsexcept for the main thread alive after the exit of main(), then you can call pthread_exit()to realize it. And any files opened inside the main thread will remain openafter its termination.

 

按照POSIX标准定义,当主线程在子线程终止之前调用pthread_exit()时,子线程是不会退出的。

 

注意:这里在main函数中调用pthread_exit()只会是主线程退出,而进程并未退出。因此新线程继续执行而没有退出。

我们可以在return 0;这条语句前面添加一条输出语句printf(“Mainthread has exited!\n”);来进行测试,输出结果不发生任何变化,说明这条语句没有被执行到。也就说明进程并未退出。

 

因此:

     一个线程的退出不会影响另外一个线程。但是进程结束,所有线程也就结束了,所有资源会被回收。

 

我们可以再写一个程序来进行验证:


4、在创建的新线程B中再次创建新线程C,那么如果B先退出,那么C将会继续执行而不会退出。

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>

pthread_t ntid;//线程ID

void printids(const char *s)
{
        pid_t pid;
        pthread_t tid;
        pid = getpid();
        tid = pthread_self();
        printf("%s pid %u tid %u (0x%x)\n",s,(unsigned int)pid,
                       (unsigned int)tid,(unsigned int)tid);
}


void *thrfun2(void *arg){
        sleep(1);//使得创建它的主线程先退出
        printids("new thread of the new thread");

        return ((void *)0);
}

void *thrfun(void *arg){
        sleep(1);//使得主线程先退出
        printids("new thread");
        int err;
        err = pthread_create(&ntid,NULL,thrfun2,NULL);

        if(err != 0)
                perror("pthread_create");

        return ((void *)0);
}

int main(){
        int err;
        err = pthread_create(&ntid,NULL,thrfun,NULL);

        if(err != 0)
                perror("pthread_create");
        printids("main thread");

        //sleep(1);

        pthread_exit(NULL);
}

运行结果:

huangcheng@ubuntu:~$ ./a.out
main thread pid 2413 tid 3077912256 (0xb77536c0)
new thread pid 2413 tid 3077909360 (0xb7752b70)
new thread of the new thread pid 2413 tid 3069516656 (0xb6f51b70)

2013-05-26 16:21:06 xiajun07061225 阅读数 21280

我们在一个线程中经常会创建另外的新线程,如果主线程退出,会不会影响它所创建的新线程呢?下面就来讨论一下。

 

1、  主线程等待新线程先结束退出,主线程后退出。正常执行。

实例代码:

#include "apue.h"
#include <pthread.h>

pthread_t ntid;//线程ID

void printids(const char *s)
{
        pid_t pid;
        pthread_t tid;
        pid = getpid();
        tid = pthread_self();
        printf("%s pid %u tid %u (0x%x)\n",s,(unsigned int)pid,
                        (unsigned int)tid,(unsigned int)tid);
}

void *thrfun(void *arg){
        //sleep(1);//使得主线程先退出
        printids("new thread");

        return ((void *)0);
}

int main(){
        int err;
        err = pthread_create(&ntid,NULL,thrfun,NULL);

        if(err != 0)
                err_quit("can't create thread: %s\n",strerror(err));
        printids("main thread");

        sleep(1);//等待新线程先结束

        exit(0);
}
运行结果:


2、  进程先退出,新线程也会立即退出,系统清除所有资源。

实例代码:

#include "apue.h"
#include <pthread.h>

pthread_t ntid;//线程ID

void printids(const char *s)
{
        pid_t pid;
        pthread_t tid;
        pid = getpid();
        tid = pthread_self();
        printf("%s pid %u tid %u (0x%x)\n",s,(unsigned int)pid,
                        (unsigned int)tid,(unsigned int)tid);
}

void *thrfun(void *arg){
        sleep(1);//使得主线程先退出
        printids("new thread");

        return ((void *)0);
}

int main(){
        int err;
        err = pthread_create(&ntid,NULL,thrfun,NULL);

        if(err != 0)
                err_quit("can't create thread: %s\n",strerror(err));
        printids("main thread");

        //sleep(1);

        exit(0);//注意是进程(不是线程)退出
}

运行结果:


可以发现主线程退出后所创建的新线程也停止运行了。


3、如果主线程调用了pthread_exit,那么它退出了,子线程也不会退出。

实例代码:

#include "apue.h"
#include <pthread.h>

pthread_t ntid;//线程ID

void printids(const char *s)
{
        pid_t pid;
        pthread_t tid;
        pid = getpid();
        tid = pthread_self();
        printf("%s pid %u tid %u (0x%x)\n",s,(unsigned int)pid,
                        (unsigned int)tid,(unsigned int)tid);
}

void *thrfun(void *arg){
        sleep(1);//使得主线程先退出
        printids("new thread");

        return ((void *)0);
}

int main(){
        int err;
        err = pthread_create(&ntid,NULL,thrfun,NULL);

        if(err != 0)
                err_quit("can't create thread: %s\n",strerror(err));
        printids("main thread");

        //sleep(1);

            pthread_exit(NULL);

        exit(0);
}
运行结果:



POSIX标准定义:

When you program with POSIX Threads API,there is one thing about pthread_exit() that you may ignore for mistake. Insubroutines that complete normally, there is nothing special you have to dounless you want to pass a return code back using pthread_exit(). The completionwon't affect the other threads which were created by the main thread of thissubroutine. However, in main(), when the code has been executed to the end,there could leave a choice for you. If you want to kill all the threads thatmain() created before, you can dispense with calling any functions. But if you want to keep the process and all the other threadsexcept for the main thread alive after the exit of main(), then you can call pthread_exit()to realize it. And any files opened inside the main thread will remain openafter its termination.

 

按照POSIX标准定义,当主线程在子线程终止之前调用pthread_exit()时,子线程是不会退出的。

 

注意:这里在main函数中调用pthread_exit()只会是主线程退出,而进程并未退出。因此新线程继续执行而没有退出。

我们可以在return 0;这条语句前面添加一条输出语句printf(“Mainthread has exited!\n”);来进行测试,输出结果不发生任何变化,说明这条语句没有被执行到。也就说明进程并未退出。

 

因此:

一个线程的退出不会影响另外一个线程。但是进程结束,所有线程也就结束了,所有资源会被回收。

 

我们可以再写一个程序来进行验证:

4、在创建的新线程B中再次创建新线程C,那么如果B先退出,那么C将会继续执行而不会退出。

实例代码:

#include "apue.h"
#include<pthread.h>

pthread_t ntid;//线程ID

void printids(const char *s)
{
        pid_t pid;
        pthread_t tid;
        pid = getpid();
        tid = pthread_self();
        printf("%s pid %u tid %u (0x%x)\n",s,(unsigned int)pid,
                       (unsigned int)tid,(unsigned int)tid);
}


void *thrfun2(void *arg){
        sleep(1);//使得创建它的主线程先退出
        printids("new thread of the new thread");

        return ((void *)0);
}

void *thrfun(void *arg){
        sleep(1);//使得主线程先退出
        printids("new thread");
        int err;
        err = pthread_create(&ntid,NULL,thrfun2,NULL);

        if(err != 0)
                err_quit("can'tcreate thread: %s\n",strerror(err));

        return ((void *)0);
}

int main(){
        int err;
        err = pthread_create(&ntid,NULL,thrfun,NULL);

        if(err != 0)
                err_quit("can'tcreate thread: %s\n",strerror(err));
        printids("main thread");

        //sleep(1);

        pthread_exit(NULL);

        printf("main thread has exited!\n");

        exit(0);
}

运行结果:






2018-11-28 17:05:50 vanturman 阅读数 3782

Linux中pthread是我们进行多线程并发时经常使用的,pthread创建的子线程和主线程的终止顺序有什么样的关系,下面通过代码来总结下。

在代码测试前,先说下结论:

      (1)主线程和子线程之间没有必然的退出次序关系。主线程退出,子线程可以继续执行;子线程退出,主线程也可以继续执行。

      (2)程序加载到内存中执行的时候,进程就会生成一个主线程。虽然主线程和子线程之间没有必然的退出次序关系,但是如果进程终止,那么进程下所有的线程都会终止。

1. 子线程先终止,主线程后终止。 

代码如下:

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

void printids(const char *str) {
    pid_t pid = getpid();
    pthread_t tid = pthread_self();
    printf("%s pid: %u, tid: %u, tid in 0x presentation: 0x%x.\n",str, (unsigned int)pid, (unsigned int)tid, (unsigned int)tid);
}

void *func(void *arg) {
    printids("descendant thread");
    pthread_detach(pthread_self());
    return ((void*)0);
}

int main(void) {
    pthread_t myid;
    pthread_create(&myid, NULL, func, NULL);
    
    sleep(1);  // 等待子线程先退出
    printids("main thread");

    return 0;
}

运行结果: 

2. 进程结束,所有线程都终止。 

代码如下:

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

void printids(const char *str) {
    pid_t pid = getpid();
    pthread_t tid = pthread_self();
    printf("%s pid: %u, tid: %u, tid in 0x presentation: 0x%x.\n",str, (unsigned int)pid, (unsigned int)tid, (unsigned int)tid);
}

void *func(void *arg) {
    sleep(1);  // 等待主线程先退出
    printids("descendant thread");
    pthread_detach(pthread_self());
    return ((void*)0);
}

int main(void) {
    pthread_t myid;
    pthread_create(&myid, NULL, func, NULL);
    
    // sleep(1);  // 等待子线程先退出
    printids("main thread");

    return 0;  //进程退出,系统清除所有资源
}

运行结果: 
 
此时,由于进程退出,该进程中的所有线程都会终止,系统回收所有的资源,因此子线程还没来得及输出pid和tid就已经退出了。

3、主线程先终止,子线程后终止。

主线程需要调用 pthread_exit() 终止,注意和示例2的区别。

代码如下:

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

void printids(const char *str) {
    pid_t pid = getpid();
    pthread_t tid = pthread_self();
    printf("%s pid: %u, tid: %u, tid in 0x presentation: 0x%x.\n",str, (unsigned int)pid, (unsigned int)tid, (unsigned int)tid);
}

void *func(void *arg) {
    sleep(1);  // 等待主线程先退出
    printids("descendant thread");
    pthread_detach(pthread_self());
    return ((void*)0);
}

int main(void) {
    pthread_t myid;
    pthread_create(&myid, NULL, func, NULL);
    
    printids("main thread");
    pthread_exit(NULL);

    return 0;  //进程退出,系统清除所有资源
}

运行结果:

When you program with POSIX Threads API,there is one thing about pthread_exit() that you may ignore for 
mistake. Insubroutines that complete normally, there is nothing special you have to dounless you want 
to pass a return code back using pthread_exit(). The completionwon’t affect the other threads which 
were created by the main thread of thissubroutine. However, in main(), when the code has been executed 
to the end,there could leave a choice for you. If you want to kill all the threads that main() created 
before, you can dispense with calling any functions. But if you want to keep the process and all the 
other threadsexcept for the main thread alive after the exit of main(), then you can call pthread_exit() 
to realize it. And any files opened inside the main thread will remain openafter its termination.

主进程退出后,子进程并没有退出,而是继续执行。

 

POSIX标准定义: 

按照POSIX标准定义,当主线程在子线程终止之前调用pthread_exit()时,子线程是不会退出的。

注意:这里在main函数中调用pthread_exit()只会是主线程退出,而进程并未退出。参照CSAPP中的讲解,当主线程执行pthread_exit()之后,主线程终止,进程并未终止,而是等待所有的子线程终止之后再结束。

总结: 

一个线程的终止不会影响到另外一个线程。但是进程结束,该进程下所有线程也会立马终止,所有资源会被回收。

2019-03-14 12:03:02 LIN1986LIN 阅读数 672

 

问题

第一题(主线程只会执行主队列的任务吗?)

let key = DispatchSpecificKey()

DispatchQueue.main.setSpecific(key: key, value: "main")

func log() {
    debugPrint("main thread: \(Thread.isMainThread)")
    let value = DispatchQueue.getSpecific(key: key)
    debugPrint("main queue: \(value != nil)")
}

DispatchQueue.global().sync(execute: log)
RunLoop.current.run()

执行结果是什么?

第二题(主队列任务只会在主线程上执行吗)

let key = DispatchSpecificKey()

DispatchQueue.main.setSpecific(key: key, value: "main")

func log() {
  debugPrint("main thread: \(Thread.isMainThread)")
  let value = DispatchQueue.getSpecific(key: key)
  debugPrint("main queue: \(value != nil)")
}

DispatchQueue.global().async {
  DispatchQueue.main.async(execute: log)
}
dispatchMain()

执行结果是什么?

解答

第一题

结果:

"main thread: true"
"main queue: false"

我们可以看swift-corelibs-libdispatch的一个PR10.6.2: Always run dispatch_sync blocks on the current thread to bett…

static void
_dispatch_barrier_sync_f_slow(dispatch_queue_t dq, void *ctxt, dispatch_function_t func)
{

    // It's preferred to execute synchronous blocks on the current thread
    // due to thread-local side effects, garbage collection, etc. However,
    // blocks submitted to the main thread MUST be run on the main thread

    struct dispatch_barrier_sync_slow2_s dbss2 = {
        .dbss2_dq = dq,
        .dbss2_func = func,
        .dbss2_ctxt = ctxt,
        .dbss2_ctxt = ctxt,     
        .dbss2_sema = _dispatch_get_thread_semaphore(),
    };
    struct dispatch_barrier_sync_slow_s {
@@ -746,17 +759,17 @@ _dispatch_barrier_sync_f_slow(dispatch_queue_t dq, void *ctxt, dispatch_function
        .dc_func = _dispatch_barrier_sync_f_slow_invoke,
        .dc_ctxt = &dbss2,
    };

    dispatch_queue_t old_dq = _dispatch_thread_getspecific(dispatch_queue_key);
    _dispatch_queue_push(dq, (void *)&dbss);
    dispatch_semaphore_wait(dbss2.dbss2_sema, DISPATCH_TIME_FOREVER);
    while (dispatch_semaphore_wait(dbss2.dbss2_sema, dispatch_time(0, 3ull * NSEC_PER_SEC))) {
        if (DISPATCH_OBJECT_SUSPENDED(dq)) {
            continue;
        }
        if (_dispatch_queue_trylock(dq)) {
            _dispatch_queue_drain(dq);
            _dispatch_queue_unlock(dq);
        }
    if (dq != dispatch_get_main_queue()) {
        _dispatch_thread_setspecific(dispatch_queue_key, dq);
        func(ctxt);
        _dispatch_workitem_inc();
        _dispatch_thread_setspecific(dispatch_queue_key, old_dq);
        dispatch_resume(dq);
    }
    _dispatch_put_thread_semaphore(dbss2.dbss2_sema);
}

It's preferred to execute synchronous blocks on the current thread      
due to thread-local side effects, garbage collection, etc.

DispatchQueue.global().sync会阻塞当前线程MainThread,那加入DispatchQueue.global的任务会在哪个线程执行呢?
苹果的解释是为了性能,因为线程切换是好性能的,在当前线程MainThread中执行任务。下面这一部分会介绍一下到底是怎样线程切换性能的原因,内容主要来自于
欧阳大哥深入iOS系统底层之CPU寄存器介绍一文,这篇文章写的非常好,个人很是喜爱,其中有这么一段:

线程切换时的寄存器复用   
我们的代码并不是只在单线程中执行,而是可能在多个线程中执行。那么这里你就可能会产生一个疑问?既然进程中有多个线程在并行执行,而CPU中的寄存器又只有那么一套,如果不加处理岂不会产生数据错乱的场景?答案是否定的。我们知道线程是一个进程中的执行单元,每个线程的调度执行其实都是通过操作系统来完成。也就是说哪个线程占有CPU执行以及执行多久都是由操作系统控制的。具体的实现是每创建一个线程时都会为这线程创建一个数据结构来保存这个线程的信息,我们称这个数据结构为线程上下文,每个线程的上下文中有一部分数据是用来保存当前所有寄存器的副本。每当操作系统暂停一个线程时,就会将CPU中的所有寄存器的当前内容都保存到线程上下文数据结构中。而操作系统要让另外一个线程执行时则将要执行的线程的上下文中保存的所有寄存器的内容再写回到CPU中,并将要运行的线程中上次保存暂停的指令也赋值给CPU的指令寄存器,并让新线程再次执行。可以看出操作系统正是通过这种机制保证了即使是多线程运行时也不会导致寄存器的内容发生错乱的问题。因为每当线程切换时操作系统都帮它们将数据处理好了。下面的部分线程上下文结构正是指定了所有寄存器信息的部分:

//这个结构是linux在arm32CPU上的线程上下文结构,代码来自于:http://elixir.free-electrons.com/linux/latest/source/arch/arm/include/asm/thread_info.h  
//这里并没有保存所有的寄存器,是因为ABI中定义linux在arm上运行时所使用的寄存器并不是全体寄存器,所以只需要保存规定的寄存器的内容即可。这里并不是所有的CPU所保存的内容都是一致的,保存的内容会根据CPU架构的差异而不同。
//因为iOS的内核并未开源所以无法得到iOS定义的线程上下文结构。

//线程切换时要保存的CPU寄存器,
struct cpu_context_save {
    __u32   r4;
    __u32   r5;
    __u32   r6;
    __u32   r7;
    __u32   r8;
    __u32   r9;
    __u32   sl;
    __u32   fp;
    __u32   sp;
    __u32   pc;
    __u32   extra[2];       /* Xscale 'acc' register, etc */
};

//线程上下文结构
struct thread_info {
    unsigned long       flags;      /* low level flags */
    int         preempt_count;  /* 0 => preemptable, <0 => bug */
    mm_segment_t        addr_limit; /* address limit */
    struct task_struct  *task;      /* main task structure */
    __u32           cpu;        /* cpu */
    __u32           cpu_domain; /* cpu domain */
    struct cpu_context_save cpu_context;    /* cpu context */
    __u32           syscall;    /* syscall number */
    __u8            used_cp[16];    /* thread used copro */
    unsigned long       tp_value[2];    /* TLS registers */
#ifdef CONFIG_CRUNCH
    struct crunch_state crunchstate;
#endif
    union fp_state      fpstate __attribute__((aligned(8)));  /*浮点寄存器*/
    union vfp_state     vfpstate;  /*向量浮点寄存器*/
#ifdef CONFIG_ARM_THUMBEE
    unsigned long       thumbee_state;  /* ThumbEE Handler Base register */
#endif
};

最后引申出个很经典的问题,就是苹果的MapKit / VektorKit,它在底层实现的时候,不仅仅要求代码执行在主线程上,还要求执行在 GCD 的主队列上。所以只是在执行的时候判断当前是不是主线程是不够的,需要判断当前是不是在主队列上,那怎么判断呢?
GCD没有提供API来进行判断当前执行任务是在什么队列,但是我们可以利用dispatch_queue_set_specific和 dispatch_get_specific这一组方法为主队列打上标记,这里是RxSwift判断是否是主队列的代码:

extension DispatchQueue {
    private static var token: DispatchSpecificKey<()> = {
        let key = DispatchSpecificKey<()>()
        DispatchQueue.main.setSpecific(key: key, value: ())
        return key
    }()

    static var isMain: Bool {
        return DispatchQueue.getSpecific(key: token) != nil
    }
}

第二题

结果:

"main thread: false"
"main queue: true"

当我把dispatchMain()删掉之后打印出来的结果是这样

"main thread: true""main queue: true"

所以可以肯定是dispatchMain()在作怪。

之后我再加这段代码

override func touchesBegan(_ touches: Set, with event: UIEvent?) {
    print("-----")
}

打印了这些

"main thread: false"
"main queue: true"
2018-08-23 14:13:19.281103+0800 MainThread[2720:5940476] [general] Attempting to wake up main runloop, but the main thread as exited. This message will only log once. Break on _CFRunLoopError_MainThreadHasExited to debug.

同时Google到这些解释

You don't generally want to use dispatch_main(). It's for things other than regular applications (system daemons and such). It is, in fact, guaranteed to break your program if you call it in a regular app.

dispatch_main() is not for running things on the main thread — it runs the GCD block dispatcher. In a normal app, you won't need or want to use it.

还查到这个是OSX服务程序使用,iOS不使用。通过上面的解释我猜测主队列任务通常是在主线程执行,但是当遇到这种主线程已经退出的情形,比如执行了dispatchMain(),苹果在底层选择让其他线程来执行主线程的任务。

在看苹果源码看到这一段swift-corelibs-libdispatch

void
dispatch_barrier_sync(dispatch_queue_t dq, void (^work)(void))
{
    // Blocks submitted to the main queue MUST be run on the main thread,
    // therefore we must Block_copy in order to notify the thread-local
    // garbage collector that the objects are transferring to the main thread
    if (dq == dispatch_get_main_queue()) {
        dispatch_block_t block = Block_copy(work);
        return dispatch_barrier_sync_f(dq, block, _dispatch_call_block_and_release);
    }   
    struct Block_basic *bb = (void *)work;
    dispatch_barrier_sync_f(dq, work, (dispatch_function_t)bb->Block_invoke);

Blocks submitted to the main queue MUST be run on the main thread

虽然不够严谨,但在iOS系统上可以说主队列任务只会在主线程上执行