iOS - 理解线程

1,299 阅读7分钟

简介

线程(Thread)就是运行在进程上下文中的逻辑流,每个线程都有自己独立的上下文,所有运行在同一个进程中的线程共享该进程的整个虚拟地址空间。

我自己时常简单理解为线程是最小的可调度单位,进程是最小的持有资源单位。

  • 线程

    • 线程上下文
    • 唯一的整数线程ID
    • 栈指针
    • 程序计数器
    • 通用目的寄存器
    • 条件码

多线程的执行模型和多进程的执行模型是相似的。每个进程开始生命周期时都是单一线程,整个线程成为主线程。某一时刻之后,主线程创建一个对等线程。

  • 线程的上下文切换要比进程上下文切换快得多
  • 进程是按照父子层次来组织的,线程是对等的,一个线程可以杀死任意对等线程

在Linux中进程和线程的描述结构是一致的

  • 线程状态

    • 新建 New
    • 就绪 Ready
    • 运行 Running
    • 终止 Terminated
    • 浅度睡眠 Interruptible
    • 深度睡眠 Uninterruptible
    • 停止 Stopped

iOS线程调度的库如图,有由BSD提供的Pthreads,Kernel提供的mach_thread,基于Pthreads封装的NSThread,以及NSOperation/NSOperationQueue和Libdispatch库实现的GCD.

Posix线程

Posix线程(Pthreads)是在C程序中处理线程的一个标准接口。允许程序创建、杀死和回收线程,与对等线程安全地共享数据,还可以通知对等线程系统状态的变化。

线程操作

创建线程

可以使用pthread_attr_t线程属性来创建pthread,如果使用默认线程属性的话,传NULL即可

int pthread_create(pthread_t _Nullable * _Nonnull __restrict,

        const pthread_attr_t * _Nullable __restrict,

        void * _Nullable (* _Nonnull)(void * _Nullable),

        void * _Nullable __restrict);
- (void)createThread {

  pthread_t thread;

  pthread_create(&thread, NULL, run, NULL);

  pthread_detach(thread);

}



void *run(void *param) {

  NSLog(@"%@", [NSThread currentThread]);

  return NULL;

}

线程属性

应用场景

// 声明 pthread_attr_t

pthread_attr_t attr;



// 初始化

pthread_attr_init(&attr);



// 设置

pthread_attr_setscope(&attr, int);                     // 作用域

pthread_attr_setstack(&attr, void * _Nonnull, size_t); // 栈

pthread_attr_setguardsize(&attr, size_t);              // 栈末尾的警戒缓冲区大小

pthread_attr_setstackaddr(&attr, void * _Nonnull);     // 栈地址

pthread_attr_setstacksize(&attr, size_t);              // 栈大小

pthread_attr_setschedparam(&attr, const struct sched_param *restrict _Nonnull); // 调度参数

pthread_attr_setdetachstate(&attr, int);               // 分离状态

pthread_attr_setschedpolicy(&attr, int);               // 调度策略

pthread_attr_setinheritsched(&attr, int);              // 继承

pthread_attr_set_qos_class_np(&attr, qos_class_t __qos_class, int __relative_priority); // 分配 QoS 类



// 创建线程时将 attr 地址传入

// pthread_create(&thread, &attr, ...);



// 使用完毕销毁

pthread_attr_destroy(&attr);

取消线程

发送成功并不意味着thread会终止

int pthread_cancel(pthread_t) __DARWIN_ALIAS(pthread_cancel);

终止线程

线程显示退出

void pthread_exit(void * _Nullable) __dead2;

回收已终止线程的资源

以阻塞的方式等待thread指定的线程结束,当函数返回时,被等待线程的资源被收回。如果线程已经结束,该函数会立即返回。thread指定的线程必须是joinable的

int pthread_join(pthread_t , void * _Nullable * _Nullable)

        __DARWIN_ALIAS_C(pthread_join);

互斥锁

sleep-waiting类型的锁

MUTEX ROUTINES

     int pthread_mutexattr_destroy(pthread _ mutexattr _ t *attr)

                        Destroy a mutex attributes object.



     int pthread_mutexattr_init(pthread _ mutexattr _ t *attr)

                        Initialize a mutex attributes object with default val-ues. values.

                        ues.



     int pthread_mutex_destroy(pthread _ mutex _ t *mutex)

                        Destroy a mutex.



     int pthread_mutex_init(pthread _ mutex _ t *mutex, const pthread _ mutexattr _ t

                        *attr)

                        Initialize a mutex with specified attributes.



     int pthread_mutex_lock(pthread _ mutex _ t *mutex)

                        Lock a mutex and block until it becomes available.



     int pthread_mutex_trylock(pthread _ mutex _ t *mutex)

                        Try to lock a mutex, but don't block if the mutex is

                        locked by another thread, including the current

                        thread.



     int pthread_mutex_unlock(pthread _ mutex _ t *mutex)

                        Unlock a mutex.

条件变量

线程进入等待列表,等待信号的唤醒

CONDITION VARIABLE ROUTINES

     int pthread_condattr_init(pthread _ condattr _ t *attr)

                        Initialize a condition variable attributes object with

                        default values.



     int pthread_condattr_destroy(pthread _ condattr _ t *attr)

                        Destroy a condition variable attributes object.



     int pthread_cond_broadcast(pthread _ cond _ t *cond)

                        Unblock all threads currently blocked on the specified

                        condition variable.



     int pthread_cond_destroy(pthread _ cond _ t *cond)

                        Destroy a condition variable.



     int pthread_cond_init(pthread _ cond _ t *cond, const pthread _ condattr _ t

                        *attr)

                        Initialize a condition variable with specified

                        attributes.



     int pthread_cond_signal(pthread _ cond _ t *cond)

                        Unblock at least one of the threads blocked on the

                        specified condition variable.



     int pthread_cond_timedwait(pthread _ cond _ t *cond, pthread _ mutex _ t *mutex,

                        const struct timespec *abstime)

                        Wait no longer than the specified time for a condition

                        and lock the specified mutex.



     int pthread_cond_wait(pthread _ cond _ t *, pthread _ mutex _ t *mutex)

                        Wait for a condition and lock the specified mutex.

读写锁

写写互斥,读写互斥,读读并发

READ/WRITE LOCK ROUTINES

     int pthread_rwlock_destroy(pthread _ rwlock _ t *lock)

                        Destroy a read/write lock object.



     int pthread_rwlock_init(pthread _ rwlock _ t *lock, const

                        pthread _ rwlockattr _ t *attr)

                        Initialize a read/write lock object.



     int pthread_rwlock_rdlock(pthread _ rwlock _ t *lock)

                        Lock a read/write lock for reading, blocking until the

                        lock can be acquired.



     int pthread_rwlock_tryrdlock(pthread _ rwlock _ t *lock)

                        Attempt to lock a read/write lock for reading, without

                        blocking if the lock is unavailable.



     int pthread_rwlock_trywrlock(pthread _ rwlock _ t *lock)

                        Attempt to lock a read/write lock for writing, without

                        blocking if the lock is unavailable.



     int pthread_rwlock_unlock(pthread _ rwlock _ t *lock)

                        Unlock a read/write lock.



     int pthread_rwlock_wrlock(pthread _ rwlock _ t *lock)

                        Lock a read/write lock for writing, blocking until the

                        lock can be acquired.



     int pthread_rwlockattr_destroy(pthread _ rwlockattr _ t *attr)

                        Destroy a read/write lock attribute object.



     int pthread_rwlockattr_getpshared(const pthread _ rwlockattr _ t *attr, int

                        *pshared)

                        Retrieve the process shared setting for the read/write

                        lock attribute object.



     int pthread_rwlockattr_init(pthread _ rwlockattr _ t *attr)

                        Initialize a read/write lock attribute object.



     int pthread_rwlockattr_setpshared(pthread _ rwlockattr _ t *attr, int

                        pshared)

                        Set the process shared setting for the read/write lock

                        attribute object.
private var moduleLock = pthread_rwlock_t()



pthread_rwlock_init(&moduleLock, nil)



 private var _modules: [String: DuConfigModule] = [:]

  private var modules: [String: DuConfigModule] {

    set {

      pthread_rwlock_wrlock(&moduleLock)

      _modules = newValue

      pthread_rwlock_unlock(&moduleLock)

    }

    get {

      pthread_rwlock_rdlock(&moduleLock)

      let modules = _modules

      pthread_rwlock_unlock(&moduleLock)

      return modules

    }

  }

自旋锁

busy-waiting类型的锁

Barrier

所有线程都达到这个栅栏时,栅栏才会放行,否则到达此处的线程会被阻塞

NSThread

变量

类变量

  class var current: Thread { get }



  class var callStackReturnAddresses: [NSNumber] { get }

  class var callStackSymbols: [String] { get }



  class var isMainThread: Bool { get }

  class var main: Thread { get }

实例变量

  var threadDictionary: NSMutableDictionary { get }



  var threadPriority: Double



  var qualityOfService: QualityOfService



  var name: String?



  var stackSize: Int



  var isMainThread: Bool { get }

  var isExecuting: Bool { get }

  var isFinished: Bool { get }

  var isCancelled: Bool { get }

方法

初始化方法

  public convenience init(target: Any, selector: Selector, object argument: Any?)



  public convenience init(block: @escaping () -> Void)

类方法

  class func detachNewThread(_ block: @escaping () -> Void)

  class func detachNewThreadSelector(_ selector: Selector, toTarget target: Any, with argument: Any?)



  class func isMultiThreaded() -> Bool



  class func sleep(until date: Date)

  class func sleep(forTimeInterval ti: TimeInterval)



  class func exit()



  class func threadPriority() -> Double

  class func setThreadPriority(_ p: Double) -> Bool

实例方法

  func cancel()   

  func start()

  func main()

源码实现

GNUStep中NSThread实现



- (void) start

{

 pthread_attr_t        attr;



// 省略Exception处理



 /* Make sure the notification is posted BEFORE the new thread starts.

  */

 gnustep_base_thread_callback();



 /* The thread must persist until it finishes executing.

  */

 RETAIN(self);



 /* Mark the thread as active while it's running.

  */

 _active = YES;



  errno = 0;

 pthread_attr_init(&attr);

 /* Create this thread detached, because we never use the return state from

  * threads.

  */

 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

 /* Set the stack size when the thread is created. Unlike the old setrlimit

  * code, this actually works.

  */

 if (_stackSize > 0)

    {

   pthread_attr_setstacksize(&attr, _stackSize);

    }

  if (pthread_create(&pthreadID, &attr, nsthreadLauncher, self))

    {

      DESTROY(self);

   [NSException raise: NSInternalInconsistencyException         format: @"Unable to detach thread (last error %@)",                  [NSError _last]];

    }

}

应用

显式创建

- (void)createNSThread {

  NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];

  [thread start];

  

  //或者 [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];

}



- (void)run {

  NSLog(@"run %@", [NSThread currentThread]);

}

隐式创建

[self performSelectorInBackground:@selector(run) withObject:nil];

Mach_Thread

thread_info

kern_return_t thread_info

(

        thread_inspect_t target_act,

        thread_flavor_t flavor,

        thread_info_t thread_info_out,

        mach_msg_type_number_t *thread_info_outCnt

);

thread_basic_info

数据结构

struct thread_basic_info {

        time_value_t  user_time;   /* user run time */

        time_value_t  system_time;  /* system run time */

        integer_t    cpu_usage;   /* scaled cpu usage percentage */

        policy_t    policy;     /* scheduling policy in effect */

        integer_t    run_state;   /* run state (see below) */

        integer_t    flags;     /* various flags (see below) */

        integer_t    suspend_count; /* suspend count for thread */

        integer_t    sleep_time;   /* number of seconds that thread

                         * has been sleeping */

};
usage_scale
/*

 *        Scale factor for usage field.

 */



#define TH_USAGE_SCALE 1000
run_state
/*

 *        Thread run states (state field).

 */



#define TH_STATE_RUNNING    1    /* thread is running normally */

#define TH_STATE_STOPPED    2    /* thread is stopped */

#define TH_STATE_WAITING    3    /* thread is waiting normally */

#define TH_STATE_UNINTERRUPTIBLE 4   /* thread is in an uninterruptible

                                         *  wait */

#define TH_STATE_HALTED     5    /* thread is halted at a

                                         *  clean point */
flags
/*

 *        Thread flags (flags field).

 */

#define TH_FLAGS_SWAPPED    0x1   /* thread is swapped out */

#define TH_FLAGS_IDLE      0x2   /* thread is an idle thread */

#define TH_FLAGS_GLOBAL_FORCED_IDLE   0x4   /* thread performs global forced idle */

应用场景

CPU使用率计算

单核CPU使用率需要再除以CPU活动内核数

thread_basic_info_t basic_info_th = (thread_basic_info_t)thinfo;

    if (!(basic_info_th->flags & TH_FLAGS_IDLE)) {

      tot_cpu = tot_cpu + basic_info_th->cpu_usage / (float) TH_USAGE_SCALE * 100.0;

    }

thread_identifier_info

数据结构

struct thread_identifier_info {

        uint64_t    thread_id;   /* system-wide unique 64-bit thread id */

        uint64_t    thread_handle; /* handle to be used by libproc */

        uint64_t    dispatch_qaddr; /* libdispatch queue address */

};

应用场景

获取线程对应的队列名称

kr = thread_info((thread_t)thread, THREAD_IDENTIFIER_INFO, info, &inOutSize);



thread_identifier_info_t idInfo = (thread_identifier_info_t)info;



dispatch_queue_t* dispatch_queue_ptr = (dispatch_queue_t*)idInfo->dispatch_qaddr;



dispatch_queue_t dispatch_queue = *dispatch_queue_ptr;



const char* queue_name = dispatch_queue_get_label(dispatch_queue);

thread_extended_info

数据结构

struct thread_extended_info {           // same as proc_threadinfo (from proc_info.h) & proc_threadinfo_internal (from bsd_taskinfo.h)

        uint64_t        pth_user_time;     /* user run time */

        uint64_t        pth_system_time;    /* system run time */

        int32_t                 pth_cpu_usage;          /* scaled cpu usage percentage */

        int32_t         pth_policy;           /* scheduling policy in effect */

        int32_t         pth_run_state;     /* run state (see below) */

        int32_t         pth_flags;       /* various flags (see below) */

        int32_t                 pth_sleep_time;         /* number of seconds that thread */

        int32_t         pth_curpri;           /* cur priority*/

        int32_t         pth_priority;      /* priority*/

        int32_t         pth_maxpriority;    /* max priority*/

        char          pth_name[MAXTHREADNAMESIZE];  /* thread name, if any */

};

thread_act

thread_get_state

数据结构



kern_return_t thread_get_state

(

        thread_read_t target_act,

        thread_state_flavor_t flavor,

        thread_state_t old_state,

        mach_msg_type_number_t *old_stateCnt

);

应用场景

获取线程调用栈



#include "backtrace.h"

#include <stdio.h>

#include <stdlib.h>

#include <machine/_mcontext.h>



#if defined __i386__

#define THREAD_STATE_FLAVOR x86_THREAD_STATE32

#define THREAD_STATE_COUNT x86_THREAD_STATE32_COUNT

#define __framePointer   __ebp



#elif defined __x86_64__

#define THREAD_STATE_FLAVOR x86_THREAD_STATE64

#define THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT

#define __framePointer   __rbp



#elif defined __arm__

#define THREAD_STATE_FLAVOR ARM_THREAD_STATE

#define THREAD_STATE_COUNT ARM_THREAD_STATE_COUNT

#define __framePointer   __r[7]



#elif defined __arm64__

#define THREAD_STATE_FLAVOR ARM_THREAD_STATE64

#define THREAD_STATE_COUNT ARM_THREAD_STATE64_COUNT

#define __framePointer   __fp



#else

#error "Current CPU Architecture is not supported"

#endif



int df_backtrace(thread_t thread, void** stack, int maxSymbols) {

  _STRUCT_MCONTEXT machineContext;

  mach_msg_type_number_t stateCount = THREAD_STATE_COUNT;

  kern_return_t kret = thread_get_state(thread, THREAD_STATE_FLAVOR, (thread_state_t)&(machineContext.__ss), &stateCount);

  if (kret != KERN_SUCCESS) {

    return 0;

  }

  int i = 0;

#if defined (__arm__) || defined (__arm64__)

  stack[i] = (void *)machineContext.__ss.__lr;

  ++i;

#endif

  void **currentFramePointer = (void **)machineContext.__ss.__framePointer;

  while (i < maxSymbols && currentFramePointer) {

    void **previousFramePointer = *currentFramePointer;

    if (!previousFramePointer) {

      break;

    }

    stack[i] = *(currentFramePointer + 1);

    currentFramePointer = previousFramePointer;

    ++i;

  }

  return i;

}

线程操作

thread_suspend

thread_resume

thread_abort

thread_abort_safely

应用场景

获取所有线程堆栈和栈内存对象的时候,挂起所有线程

void ksmc_suspendEnvironment()

{

#if KSCRASH_HAS_THREADS_API

    KSLOG_DEBUG("Suspending environment.");

    kern_return_t kr;

    const task_t thisTask = mach_task_self();

    const thread_t thisThread = (thread_t)ksthread_self();



    if((kr = task_threads(thisTask, &g_suspendedThreads, &g_suspendedThreadsCount)) != KERN_SUCCESS)

    {

        KSLOG_ERROR("task_threads: %s", mach_error_string(kr));

        return;

    }



    for(mach_msg_type_number_t i = 0; i < g_suspendedThreadsCount; i++)

    {

        thread_t thread = g_suspendedThreads[i];

        if(thread != thisThread && !isThreadInList(thread, g_reservedThreads, g_reservedThreadsCount))

        {

            if((kr = thread_suspend(thread)) != KERN_SUCCESS)

            {

                // Record the error and keep going.

                KSLOG_ERROR("thread_suspend (%08x): %s", thread, mach_error_string(kr));

            }

        }

    }



    KSLOG_DEBUG("Suspend complete.");

#endif

}

异常端口

详情请参见我之前的两篇博客《APM - iOS Crash监控 原理浅析》和《APM - iOS Crash监控 XNU 异常处理》

thread_set_exception_ports

thread_get_exception_ports

thread_swap_exception_ports

NSOperation/NSOperationQueue

详情请参见我之后的博客《NSOperation/NSOperationQueue源码解析》

GCD

详情请参见我之后的博客《libdispatch源码解析》

其他

Pthread和mach_thread如何转换?

func machThread(from thread: Thread) -> thread_t {

  var count: mach_msg_type_number_t = 0

  var threads: thread_act_array_t!

   

  guard task_threads(mach_task_self_, &(threads), &count) == KERN_SUCCESS else {

    return mach_thread_self()

  }

   

  if thread.isMainThread {

    return threads[0]

  }

   

  let originName = thread.name

  defer {

    thread.name = originName

  }

  let newName = String(Int(Date.init().timeIntervalSince1970))

  thread.name = newName

  for i in 0..<count {

    let machThread = threads[Int(i)]

    if let p_thread = pthread_from_mach_thread_np(machThread) {

      var name: [Int8] = Array<Int8>(repeating: 0, count: 128)

      pthread_getname_np(p_thread, &name, name.count)

      if thread.name == String(cString: name) {

        return machThread

      }

    }

  }

  return mach_thread_self()

}

NSThread是基于Pthread封装的吗?

  • 从GNUStep的源码实现来看,是调用了Pthreads的方法
  • 从Time Profiler来看,最终调用函数是_pthread_start
  • 但是Hook了pthread_create之后,发现通过NSThread创建的时候并没有调用pthread_create

thread_self需要注意什么?

thread_t thread_self()

{

  thread_t thread_self = mach_thread_self();

  mach_port_deallocate(mach_task_self(), thread_self);

  return thread_self;

}

引用

iOS Manual Pages:developer.apple.com/library/arc…