当前iOS的多线程方式分为4中

  • pthreads
  • NSThread
  • GCD
  • NSOperation \& NSOperationQueue

pthreads

定义

POSIX线程(英语:POSIX Threads,常被缩写为Pthreads)是POSIX线程标准,定义了创建和操纵线程的一套API

实现POSIX 线程标准的库常被称作Pthreads,一般用于Unix-like POSIX 系统,如LinuxSolaris。但是Microsoft Windows上的实现也存在,例如直接使用Windows API实现的第三方库pthreads-w32;而利用Windows的SFU/SUA子系统,则可以使用微软提供的一部分原生POSIX API。

最初的Phtreads API 是ANSI/IEEE POSIX 1003.1 - 1995 standard制定的。POSIX标准不断发展并经过修订,包括Pthreads规范

该标准的副本可以从IEEE购买或从其他网站在线免费下载。

构成Pthreads API的子程序可以分为四个主要组成部分

  • 1.Thread management:线程直接工作的例程 - 创建,分离,加入等。它们还包括设置/查询线程属性(可连接,调度等)的功能,
  • 2.Mutexes:处理同步的功能,称为“互斥”。互斥体功能用于创建,销毁,锁定和解锁互斥体。这些由互斥体属性函数补充,它们设置或修改与互斥体相关联的属性。
  • 3.Condition variables:解决共享互斥体的线程之间的通信的功能。基于程序员指定的条件。该组包括基于指定的变量值创建,销毁,等待和发信号的功能。还包括设置/查询条件变量属性的函数。
  • 4.Synchronization:管理读/写锁和障碍的功能。

命名约定:线程库中的所有标识符都以pthread_开头。例子如下所示。

| 前缀 | 函数组 | | --- | --- | | pthread_ | 线程本身和其他 | | pthread_attr_ | 线程属性对象 | | pthread_mutex_ | 互斥器 | | pthread_mutexattr_ | 互斥器属性对象 | | pthread_cond_ | 条件变量 | | pthread_condattr_ | 条件属性对象 | | pthread_key_ | 线程专用数据键 | | pthread_rwlock_ | 读写锁 | | pthread_barrier_ | 同步阻塞 |

应用

创建和销毁线程示例

 
#include <pthread.h>
#include <stdio.h>
#define NUM_THREADS     5
void *PrintHello(void *threadid)
{
  long tid;
  tid = (long)threadid;//将void* 转成long
  printf("Hello World! It's me, thread #%ld!\n", tid);
  pthread_exit(NULL);//终止当前线程
}
int main (int argc, char *argv[])
 {
    pthread_t threads[NUM_THREADS];
    int rc;
    long t;
    for(t=0; t<NUM_THREADS; t++){
       printf("In main: creating thread %ld\n", t);
       rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
       if (rc){
          printf("ERROR; return code from pthread_create() is %d\n", rc);
          exit(-1);
       }
    }
    /* Last thing that main() should do */
    pthread_exit(NULL);//在main所创建的线程执行完成之前保持main的存在
 }

程序说明

一.Terminating Threads \& pthread_exit():

线程终止的方式可能是

1.线程执行完成了对应的任务(上述代码中的是PrintHello),并且返回了正确的执行结果

2.线程自己执行了pthread_exit(),此时不管线程的状态和执行状态都会结束

3.其他的线程执行了pthread_exit(),从而结束了当前线程

4.整个进程结束了,执行了exec或者exit

5.main函数提前结束了,那么无需调用pthread_exit线程都会结束

pthread_exit()函数允许程序员指定一个可选的结束装填参数,这个可选的参数会返回给进入终止状态的线程。

在正常执行完成的子程序中,您通常可以省去调用pthread_exit() - 除非当然要将可选状态代码传回。

资源清理:pthread_exit()方法不会做文件的关闭操作,线程执行完成打开的文件还是打开

在主函数调用pthread_exit()的讨论

1.如果主线程在自己生成的子线程执行完成之前已经结束,而没有调用pthread_exit(),那么就会有个严重的问题,主线程创建的所有子线程都将被终止,因为main已经结束了,没有办法再支持子线程完成任务了。

2.通过在主函数的最后调用pthread_exit(),可以使主函数阻塞,支撑子线程完成特定的任务之后再结束。

线程的参数传递

  • pthread_create()方法允许程序员将一个参数传递给线程执行的功能函数。如果需要传递多个参数,可以创建一个包含所有参数的结构体,实例化结构体将结构体的地址传递给线程的功能函数,
  • 所有的参数传递都必须转化成(void *)类型

```

```

```

//Thread Argument Passing ```

```

//传递一个整数类型,每个线程传递时使用的独特的数据结构,以此来确保在线程执行的过程中传递的参数保持完好 ```

```

long taskids[NUM_THREADS]; ```

```

​ ```

```

for(t=0; t<NUM_THREADS; t++) ```

```

{ ```

```

taskids[t] = t; ```

```

printf("Creating thread %ld\n", t); ```

```

rc = pthread_create(&threads[t], NULL, PrintHello, (void *) taskids[t]); ```

```

... ```

```

} ```

```

```

```

//此示例显示如何通过结构设置/传递多个参数,每个thread收到的都是独立的结构体对象 ```

```

include <pthread.h>

```

```

include <stdio.h>

```

```

include <stdlib.h>

```

```

//定义宏 ```

```

define NUM_THREADS 8

```

```

//声明char 类型的数组用于保存传递的message ```

```

char *messages[NUM_THREADS]; ```

```

//定义传递的结构体 ```

```

struct thread_data ```

```

{ ```

```

int thread_id; ```

```

int sum; ```

```

char *message; ```

```

}; ```

```

//定义结构体数组 ```

```

struct thread_data thread_data_array[NUM_THREADS]; ```

```

​ ```

```

void PrintHello(void threadarg) ```

```

{ ```

```

int taskid, sum; ```

```

char *hello_msg; ```

```

struct thread_data *my_data; ```

```

​ ```

```

sleep(1); ```

```

my_data = (struct thread_data *) threadarg; ```

```

taskid = my_data->thread_id; ```

```

sum = my_data->sum; ```

```

hello_msg = my_data->message; ```

```

printf("Thread %d: %s Sum=%d\n", taskid, hello_msg, sum); ```

```

pthread_exit(NULL); ```

```

} ```

```

​ ```

```

int main(int argc, char *argv[]) ```

```

{ ```

```

pthread_t threads[NUM_THREADS]; ```

```

int *taskids[NUM_THREADS]; ```

```

int rc, t, sum; ```

```

​ ```

```

sum=0; ```

```

messages[0] = "English: Hello World!"; ```

```

messages[1] = "French: Bonjour, le monde!"; ```

```

messages[2] = "Spanish: Hola al mundo"; ```

```

messages[3] = "Klingon: Nuq neH!"; ```

```

messages[4] = "German: Guten Tag, Welt!"; ```

```

messages[5] = "Russian: Zdravstvytye, mir!"; ```

```

messages[6] = "Japan: Sekai e konnichiwa!"; ```

```

messages[7] = "Latin: Orbis, te saluto!"; ```

```

​ ```

```

for(t=0;t<NUM_THREADS;t++) { ```

```

sum = sum + t; ```

```

thread_data_array[t].thread_id = t; ```

```

thread_data_array[t].sum = sum; ```

```

thread_data_array[t].message = messages[t]; ```

```

printf("Creating thread %d\n", t); ```

```

rc = pthread_create(&threads[t], NULL, PrintHello, (void *) ```

```

&thread_data_array[t]); ```

```

if (rc) { ```

```

printf("ERROR; return code from pthread_create() is %d\n", rc); ```

```

exit(-1); ```

```

} ```

```

} ```

```

pthread_exit(NULL); ```

```

} ```

```

```

```

//错误的数据传递 ```

```

int rc; ```

```

long t; ```

```

​ ```

```

for(t=0; t<NUM_THREADS; t++) ```

```

{ ```

```

printf("Creating thread %ld\n", t); ```

```

rc = pthread_create(&threads[t], NULL, PrintHello, (void *) &t); ```

```

... ```

```

} ```

```

//传递的是t的地址,而t存在的是共享内存空间,并且被所有线程可见,随着程序循环的进行t的内存地址极有可能发生改变,而如果这时创建thread函数再去使用t的地址就会出现严重的问题。 ```

线程的连接和分离

方法列表

pthread_join (threadid,status)

pthread_detach (threadid)

pthread_attr_setdetachstate (attr,detachstate)

pthread_attr_getdetachstate (attr,detachstate)

线程的连接

  • 连接是完成线程同步的一种方式

joining

  • pthread_join()方法阻止调用线程,直到指定的线程线程终止。
  • 如果在目标线程中调用pthread_exit()来指定它,那么我们就可以得到目标线程的终止返回装填。
  • 一个正在连接的线程只能接受一次pthread_join(),在同一个线程上尝试多个连接是一个逻辑错误
  • 另外两个同步方法,互斥体和条件变量将在后面讨论

是否是可连接的?

  • 线程被创建时,有一个属性来决定该线程是否为可连接的或分离的。只有创建的时候指定是可连接属性的线程才能被连接。如果一个线程创建的时候设置的是分离的,那么该线程永远都不能进行连接。
  • POSIX标准的最终草案规定,应将线程创建为可连接。
  • 要将线程显示地创建为可连接或分离的,需要传递pthread_create()的attr参数,四步过程是:

1.声明pthread_attr_t数据类型的pthread属性对象

2.通过pthread_attr_init()初始化属性对象

3.使用pthread_attr_setdetachstate()设置属性分离状态

4.结束的时候,使用pthread_attr_destroy()方法释放属性对象使用的资源

分离

  • pthread_detach()方法可以用于显示的分离一个线程,计算是这个线程创建的时候是joinable的

建议

  • 如果线程需要加入,请考虑将其明确创建为可连接。这提供了可移植性,因为并不是所有的实现可能会将线程默认地创建为可连接的。
  • 如果事先知道一个线程永远不需要与另一个线程连接,请考虑将其明确创建为分离状态。某些系统资源可以被节省下来。

Thread Joining example

 
 
https://computing.llnl.gov/tutorials/pthreads/#CreatingThreads

0 Comments latest

No comments.