11.4 线程创建

传统UNIX程序模型中每一个进程都只支持一个线程控制,在概念上,这与每个线程仅仅由一个线程组成的线程模型是一样的。采用pthreads以后,当一个程序运行的时候,系统也会启动一个单线程控制进程,当程序运行的时候,其行为与传统进程并没有什么明显区别,除非它创建了多线程控制,其他线程可以通过调用函数pthread_create来创建。

  1. #include <pthread.h>
  2. int pthread_create(pthread_t *restrict tidp,
  3. const pthread_attr_t *restrict attr,
  4. void *(*start_rtn)(void *), void *restrict arg);
  5. Returns: 0 if OK, error number on failure.

tidp指针指向的内存地址在函数pthread_create成功返回以后将被设置为新创建线程的线程ID,参数attr用于自定义线程属性,我们将在12.3中讲述线程属性,现在,我们只是向其传递NULL,以创建一个具有默认属性的线程。

新创建的线程会从地址start_rtn开始运行,该函数仅仅带有一个参数,arg,是一个无符号指针。如果你需要传递多余一个参数,那么你就需要将这些参数存储到一个结构中,并传递该结构的地址到arg.

当一个新线程被创建的时候,哪一个线程先运行并没有任何保证,新创建的线程拥有对于进程地址空间的访问权限,并且继承了调用线程的floating-point environment以及信号掩码,但是挂起状态的信号将会被清除。

注意,pthread的函数在出现错误的时候仅仅返回一个错误码,它们并不会像其他POSIX函数一样设置errno.每一个线程都有一个errno的copy的目的是保证使用errno函数的兼容性。with threads,it is cleaner to return the error code from the function,thereby restriciting the scope of the error to the function that caused it, instead of relying on some global state that is changed as a side of the function.

Example

虽然没有一个可移植的打印进程ID的方式,我们可以写一个小程序,来看一下线程是如何工作的。在图11.2中的程序创建了一个线程并且打印了新创建线程与原来线程的线程ID以及进程ID。

  1. #include "apue.h"
  2. #include <pthread.h>
  3. pthread_t ntid;
  4. void printids(const char *s)
  5. {
  6. pid_t pid;
  7. pthread_t tid;
  8. pid = getpid();
  9. tid = pthread_self();
  10. printf("%s pid %lu tid %lu (0x%lx)\n", s, (unsigned long)pid,
  11. (unsigned long)tid, (unsigned long)tid);
  12. }
  13. void *thr_fn(void *arg)
  14. {
  15. printids("new thread: ");
  16. return ((void *)0);
  17. }
  18. int main(void)
  19. {
  20. int err;
  21. err = pthread_create(&ntid, NULL, thr_fn, NULL);
  22. if(err != 0)
  23. {
  24. err_exit(err, "cann‘t create thread");
  25. }
  26. printids("main thread: ");
  27. sleep(1);
  28. exit(0);
  29. }

图11.2打印线程ID

该程序有两个特别之处,这对于处理主线程与新线程之间的竞态条件是必须的。我们将在本章后面学到更好的竞态条件处理方法。

  1. 主线程需要睡眠,如果主线程睡眠,主线程就会结束运行退出,因此在新进程开始运行之前,可能整个进程就已经终止了,这一行为与操作系统的线程实现以及调度算法有关。
  2. 新线程通过调用函数pthread_self函数来获取其自身线程ID,而不是通过从共享内存中读取或者是通过其线程运行函数的参数来获取,在上文中我们提到,pthread_create将会返回新创建线程的线程ID到第一个参数中(tidp),在我们的例子中,主线程将会存储子线程ID到ntid中,但是新线程并不能保证可以安全地使用,因为如果新线程在主线程之前开始运行,而主线程还没有从pthread_create函数中返回的时候,新线程就会看到一个未初始化的ntid实体,而不是正确的线程ID.

运行效果如下图所示:

  1. [email protected]:~/UnixProgram/Chapter11$ ./11_2Exe
  2. main thread: pid 2340 tid 3077592768 (0xb77056c0)
  3. new thread: pid 2340 tid 3077589872 (0xb7704b70)
  4. [email protected]:~/UnixProgram/Chapter11$

可以看到运行结果与预期一致,两个线程拥有相同的进程ID,但是却拥有不同的线程ID.

来自为知笔记(Wiz)

时间: 2024-10-10 15:43:51

11.4 线程创建的相关文章

基础笔记11(线程)

有如下说法(Synchronized的内存可见性) 在单一线程中,只要重排序不会影响到程序的执行结果,那么就不能保证其中的操作一定按照程序写定的顺序执行,即使重排序可能会对其它线程产生明显的影响. 在Java内存模型下,每个线程都有它自己的工作内存(主要是CPU的cache或寄存器),它对变量的操作都在自己的工作内存中进行,而线程之间的通信则是通过主存和线程的工作内存之间的同步来实现的. 导致共享数据出错的两个问题:重排序:共享数据及时更新. synchronized(M){//}的使用: 当T

iOS开发——多线程OC篇&amp;(二)线程创建

创建线程 一.创建和启动线程简单说明 一个NSThread对象就代表一条线程 创建.启动线程 (1) NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil]; [thread start]; // 线程一启动,就会在线程thread中执行self的run方法 主线程相关用法 + (NSThread *)mainThread; // 获得主线程 - (BOOL)isMa

015 线程创建

线程  ● 进程启动 ○ 进程内核对象 进程空间 ○ 线程 ● 可以通过IDE设置入口函数 ● 自己创建线程 ○ 创建线程函数 CreateThread() ○ 新建线程内核对象(结构体) ● 线程 ○ 分配一块内存空间,作为当前线程的堆栈 ○ 两条在同一进程内线程,都是隔离的 ○ 线程他们是封闭的,每个线程都有自己的一个堆栈 ○ 但是都是属于当前进程内的堆栈 ○ 进程赋予了线程交互的能力 ● 线程创建 1 HANDLE WINAPI CreateThread( 2 _In_opt_ LPSEC

基于C++11的线程池

1.封装的线程对象 class task : public std::tr1::enable_shared_from_this<task> { public: task():exit_(false){} task( const task & ) = delete; ~task(){} task & operator =( const task &) = delete; void start(); void stop() { exit_ = true; sync_.not

MFC 线程创建方式

MFC 分UI线程和工作线程,一般现在的应用程序都是一个主UI线程和N个工作线程来完成工作.主UI线程获取到工作线程发送的信息来刷新界面. 不过因为工作需要,MFC有要维护的项目,因此就学习一下MFC创建UI线程,使用工作线程的方式. 1.UI线程,继承CWinThread类  1 class CAddDeviceApp : public CWinThread 2 { 3     DECLARE_DYNCREATE(CAddDeviceApp) 4 protected: 5     CAddDe

windows系统调用 线程创建

1 #include "windows.h" 2 #include "iostream" 3 using namespace std; 4 5 class CWorkerThread{ 6 public: 7 CWorkerThread(LPCTSTR szName): 8 m_szName(szName),m_hThread(INVALID_HANDLE_VALUE){ 9 m_hThread=CreateThread( 10 NULL, 11 0, 12 Thr

多线程【Thread、线程创建】

主线程:执行主方法的线程,就叫做主线程 单线程程序:程序从mani开始从上到下依次运行 程序从main方法开始运行,JVM运行main方法,会找操作系统 开辟一条通向cpu的执行路径,cpu可以通过这条路径来执行main方法 这条路径有一个名字叫主(main)线程 创建线程方式一继承Thread类 实现步骤: 1.创建Thread类的子类 2.重写Thread类中的run方法,设置线程的任务 3.创建Thread类的子类对象 4.调用Thread类中的start方法开启一个新的线程,执行run方

Java基础加强之多线程篇(线程创建与终止、互斥、通信、本地变量)

线程创建与终止 线程创建 Thread类与Runnable接口的关系 public interface Runnable { public abstract void run(); } public class Thread implements Runnable { /* What will be run. */ private Runnable target; ...... /** * Causes this thread to begin execution; the Java Virtu

11 java 线程池 使用实例

在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间. 那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务? 在Java中可以通过线程池来达到这样的效果. 1 线程池做什么 网络请求通常有两种形式: 第一种,请求不是很频繁,而且每次连接后会保持相当一段时间来读数据或