当前位置: 代码迷 >> 综合 >> POSIX 线程编程指南(二)
  详细解决方案

POSIX 线程编程指南(二)

热度:27   发布时间:2024-01-09 03:51:13.0

线程控制

创建和终止线程

  例程:

  • pthread_create (thread,attr,start_routine,arg)

    pthread_exit (status)

    pthread_cancel (thread)

    pthread_attr_init (attr)

    pthread_attr_destroy (attr)


int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
(注:原文通篇没有参数类型,对于一些必要的接口,我会从http://man7.org/上面copy后粘贴与此)

 创建线程:

  • 开始,主程序(main()函数)会包含一个默认的线程,所有其它线程必须由程序员显式创建.

  • pthread_create创建新线程并让它可执行. 这个例程可在代码中的任意位置多次被调用执行.

  • pthread_create 参数:
    • thread: 新线程ID(唯一的).
    • attr:设置县城属性的属性对象.可以使用一个属性对象,也可以使用NULL表示默认值.
    • start_routine: 线程创建后要执行的函数(函数指针).
    • arg: 传递给start_routine的唯一参数. 必须被转换为void*,如果没有要传递的,就使用NULL.

  • 进程能创建的线程数量因平台而不同。企图超出这个限制会导致失败或者其他意想不到的结果.

查询和设置实现的线程限制-Linux示例.展示了查询默认(软件)限制并设置硬件限制的进程(包括线程)最大数量.然后验证限制已经被重写.
bash / ksh / sh tcsh / csh
$ ulimit -a
core file size          (blocks, -c) 16
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 255956
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) unlimited
cpu time               (seconds, -t) unlimited
max user processes              (-u) 1024
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited$ ulimit -Hu
7168$ ulimit -u 7168$ ulimit -a
core file size          (blocks, -c) 16
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 255956
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) unlimited
cpu time               (seconds, -t) unlimited
max user processes              (-u) 7168
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
% limit 
cputime      unlimited
filesize     unlimited
datasize     unlimited
stacksize    unlimited
coredumpsize 16 kbytes
memoryuse    unlimited
vmemoryuse   unlimited
descriptors  1024 
memorylocked 64 kbytes
maxproc      1024% limit maxproc unlimited% limit
cputime      unlimited
filesize     unlimited
datasize     unlimited
stacksize    unlimited
coredumpsize 16 kbytes
memoryuse    unlimited
vmemoryuse   unlimited
descriptors  1024 
memorylocked 64 kbytes
maxproc      7168
  • 一旦被创建,线程是对等的,并且能够创建其他线程。线程间并没有继承或者依赖.


 线程属性:

    • 默认,线程被使用特定的属性创建.其中一些属性能被程序员通过线程属性对象改变

    • 初始化与销毁属性对象的两个函数:pthread_attr_init 和 pthread_attr_destroy.

    • 其余的接口用来查询/设置线程属性对象中的特定属性.这些属性包括:
      • Detached or joinable state
      • Scheduling inheritance
      • Scheduling policy
      • Scheduling parameters
      • Scheduling contention scope
      • Stack size
      • Stack address
      • Stack guard (overflow) size

    • 其中一些属性稍后讨论.

 Thread Binding and Scheduling:

  Question: 线程被创建后,你如何知道
 a)它在何时被操作系统调度?
 b)它将在哪个处理/内核上运行? 

如果没有使用pthreads的调度机制,何时、何地执行线程由实现或者操作系统决定.一个健壮的程序绝不能依赖固定的线程执行顺序或者特定的处理器/核心

  • Pthreads API 提供了几个例程可被用于指定线程执行时如何被调度. 例如, 线程能以 FIFO (first-in first-out), RR (round-robin) 或者 OTHER (取决于操作系统)的方式运行. 它也提供了设置线程调度优先级值的能力

  • 本文不包含这些主题,但是,关于Linux的 “how things work” 的概览可以在 sched_setscheduler 的用户页(man page)找到.

  • Pthreads API 没有提供绑定线程到特定cpus/cores的例程(接口). 但是,一些本地实现可能包含此功能,比如提供非标准的 pthread_setaffinity_np例程. 注意,名字中的 “_np” 意思是"non-portable".

  • 操作系统也可能提供了方法. 例如,linux提供了 sched_setaffinity 例程.

 Terminating Threads & pthread_exit():

  • 线程能以如下几种方式终止(terminated):

    • 线程正常从它的起始例程返回,工作完成.

    • 线程调用 pthread_exit 子例程- 不管它是否完工

    • 线程被其他线程通过pthread_cancel 例程取消.

    • 整个进程通过调用 exec() 或者 exit() 终止

    • 如果 main() 先完成,不用自己显式调用 pthread_exit

  • pthread_exit() 例程允许程序员指定一个可选的终结状态参数. 可选参数在典型情况下会返回给 “joining” 此线程的线程..

  • 对于正常执行结束的子例程,可以不调用 pthread_exit() – 除非你想取回可选的状态码..

  • Cleanup: pthread_exit() 例程不会关闭文件; 任何在线程中打开的文件在线程结束后仍将保持打开状态.

  • Discussion on calling pthread_exit() from main():
    • 如果main()在它产生的线程前结束而没有显式调用pthread_exit(),那么肯定会有问题.它创建的所有线程都会终结,因为main()完成了,不会继续存在以支持哪些线程.
    • 让 main() 在最后显式调用 pthread_exit() , main() 会阻塞并保持到它所创建的线程都完成.

Example: Pthread Creation and Termination

  • 下面的示例使用 pthread_create() 例程创建了5个线程. 每个线程都打印 "Hello World!" 消息, 然后调用 pthread_exit()终结.
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS	5void *PrintHello(void *threadid)
{long tid;tid = (long)threadid;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);
}
输出:
In main: creating thread 0
In main: creating thread 1
Hello World! It's me, thread #0!
In main: creating thread 2
Hello World! It's me, thread #1!
Hello World! It's me, thread #2!
In main: creating thread 3
In main: creating thread 4
Hello World! It's me, thread #3!
Hello World! It's me, thread #4!

传递参数给线程

  • pthread_create() 例程允许程序员传递一个参数给线程的开始例程. 对于必须传递多个参数的情况,可以创建一个包含所有参数的结构体来克服这个限制, 并在pthread_create()例程中传递一个指向这个结构体实例的指针.

  • 所有的参数必须以引用方式传递(传地址)并转换成 (void *).
  Question:如何安全传递数据给新创建的线程(考虑到它们非确定性的启动和调度)? 

确保所有的数据都是线程安全的,也就是说,不能在该线程不知情的情况下被别的线程修改

  Example 1 - Thread Argument Passing (传递简单的整形变量)
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS	8char *messages[NUM_THREADS];void *PrintHello(void *threadid)
{long taskid;sleep(1);taskid = (long) threadid;printf("Thread %d: %s\n", taskid, messages[taskid]);pthread_exit(NULL);
}int main(int argc, char *argv[])
{
pthread_t threads[NUM_THREADS];
long taskids[NUM_THREADS];
int rc, t;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: Zdravstvuyte, mir!";
messages[6] = "Japan: Sekai e konnichiwa!";
messages[7] = "Latin: Orbis, te saluto!";for(t=0;t<NUM_THREADS;t++) {taskids[t] = t;printf("Creating thread %d\n", t);rc = pthread_create(&threads[t], NULL, PrintHello, (void *) taskids[t]);if (rc) {printf("ERROR; return code from pthread_create() is %d\n", rc);exit(-1);}}pthread_exit(NULL);
}
输出:
Creating thread 0
Creating thread 1
Creating thread 2
Creating thread 3
Creating thread 4
Creating thread 5
Creating thread 6
Creating thread 7
Thread 0: English: Hello World!
Thread 1: French: Bonjour, le monde!
Thread 2: Spanish: Hola al mundo
Thread 3: Klingon: Nuq neH!
Thread 4: German: Guten Tag, Welt!
Thread 5: Russian: Zdravstvytye, mir!
Thread 6: Japan: Sekai e konnichiwa!
Thread 7: Latin: Orbis, te saluto!
  Example 2 - Thread Argument Passing(通过结构体传递多个变量)
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS	8char *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);
}
Creating thread 0
Creating thread 1
Creating thread 2
Creating thread 3
Creating thread 4
Creating thread 5
Creating thread 6
Creating thread 7
Thread 0: English: Hello World!  Sum=0
Thread 1: French: Bonjour, le monde!  Sum=1
Thread 2: Spanish: Hola al mundo  Sum=3
Thread 3: Klingon: Nuq neH!  Sum=6
Thread 4: German: Guten Tag, Welt!  Sum=10
Thread 5: Russian: Zdravstvytye, mir!  Sum=15
Thread 6: Japan: Sekai e konnichiwa!  Sum=21
Thread 7: Latin: Orbis, te saluto!  Sum=28
 Example 3 - Thread Argument Passing (错误的传递方式)
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS     8void *PrintHello(void *threadid)
{long taskid;sleep(1);taskid = *(long *)threadid;printf("Hello from thread %ld\n", taskid);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("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);}}pthread_exit(NULL);
}
输出:
Creating thread 0
Creating thread 1
Creating thread 2
Creating thread 3
Creating thread 4
Creating thread 5
Creating thread 6
Creating thread 7
Hello from thread 140737488348392
Hello from thread 140737488348392
Hello from thread 140737488348392
Hello from thread 140737488348392
Hello from thread 140737488348392
Hello from thread 140737488348392
Hello from thread 140737488348392
Hello from thread 140737488348392