QNX学习笔记3 线程和进程 threads and processes

一个process中可以有好多个thread,那为什么还需要多process,因为

  1. 分隔和模块化(decoupling and modularity)
  2. 可维护性 (maintainability)
  3. 可靠性(reliability)

把问题分解开来正是 QNX Neutrino 的核心理念 一个Neutrino系统由许多个独立的模块组成 每个都有特定的任务

不同process的thread不可以共享内存(memory)

一、starting a process

接下来的东西。。我也有点看不懂了。

system()创建process 如同 command line 的功能一样

exec()family 函数 可将一个进程转换到另一个进程,且process ID 不变

spwan()family 函数 创建另一个进程(with a new process ID)

fork()函数可以创建一个和当前进程一模一样的进程(大部分一致,也有一些不一样的地方),区分的方法是判断函数返回值 在新建子进程中 返回值是0 在父进程中 返回值是子进程ID
fork()不适用于多线程 会返回ENOSYS错误 原因大概是一种保护数据的安全机制

二、starting a thread

一般通过 pthread_create()

#include

int pthread_create( pthread_t* thread,
const pthread_attr_t* attr,
void* (start_routine)(void ),
void* arg );

thread
a pointer to a pthread_t where the thread ID is stored 可设为NULL
attr
an attributes structure 可设为NULL
start_routine
the routine where the thread begins 就是thread从哪里开始,也就是说跳转到什么函数
arg
an argument passed to the thread’s start_routine 给跳转到的函数传递的参数

the thread attributes structure (pthread_create 中的第二个参数)

数据结构定义为:

typedef struct {
	int __flags;
	size_t __stacksize;
	void *__stackaddr;
	void (*__exitfunc)(void *status);
	int __policy;
	struct sched_param __param;
	unsigned __guardsize;
} pthread_attr_t;

有关这个结构的函数有许多,都是形如 pthread_attr_### 的,大致分为以下几种:
1. thread attribute administration

在使用thread attribute structure这个数据结构之前必须对其进行初始化。

...
pthread_attr_t attr;
...
pthread_attr_init (&attr);

2. the “flags” thread attribute

pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE)

表示创建一个可结合的(joinable)的thread 可以理解为 另一个thread可以通过 pthread_join() 函数在这个thread结束时开始(个人理解。。。感觉可能不对)

与之相反的是detached thread 把参数PTHREAD_CREATE_JOINABLE改为PTHREAD_CREATE_DETACHED 即可

pthread_attr_setinheritsched (&attr, PTHREAD_INHERIT_SCHED);

表示创建的子thread继承了父thread的调度策略和优先级,与之相反的是把参数PTHREAD_INHERIT_SCHED 改为 PTHREAD_EXPLICIT_SCHED;然后你得使用

pthread_attr_setschedparam()
pthread_attr_setschedpolicy()

去设置子thread的调度策略和优先级

还有一个pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);作者说你不会再Neutrino中用到它的。。放弃吧

3. the stack thread attributes

设置堆栈属性的函数数据类型如下所示:

int
pthread_attr_setguardsize (pthread_attr_t *attr, size_t gsize);
int
pthread_attr_setstackaddr (pthread_attr_t *attr, void *addr);
int
pthread_attr_setstacksize (pthread_attr_t *attr, size_t ssize);
int
pthread_attr_setstacklazy (pthread_attr_t *attr, int lazystack);

它们第一个参数都是attr,第二个参数如下:

gsize
The size of the “guard” area. //guard area是一种堆栈溢出检查机制,如果thread在guard area中写入数据的话,就会返回SIGSEGV表示堆栈溢出,如果gsize的值是0的话,那么就没有溢出检查
addr
The address of the stack, if you’re providing one. //stack的地址 指定地址有助于深度分析,使memory使用率最大化
ssize
The size of the stack.
lazystack
Indicates if the stack should be allocated on demand or up front from physical memory.(俺没看懂)

4. the scheduling thread attributes

int
pthread_attr_setschedparam (pthread_attr_t *attr,
const struct sched_param *param);
int
pthread_attr_setschedpolicy (pthread_attr_t *attr,
int policy);

policy 的取值是 SCHED_FIFO, SCHED_RR, SCHED_OTHER 三者之一

param 中包含 sched_priority 直接设置它就行了

三、a few example

#include 
#include 
#include 

void*  new_thread( void*  arg )
{
   printf("ha ha ha\n");
   printf( "This is thread %d\n", pthread_self() );
   printf("wo yun xing le%d\n", arg);
   return( 0 );
}

int main( void )
{
   pthread_create(NULL,NULL,new_thread,(void *)123);
   printf("zhun bei shui 10 s\n");
   sleep(5);
//   printf("!!!\n");
   return EXIT_SUCCESS;
}

上面就是最简单的 创建子线程方法 在console中输出结果如下:
ha ha ha
This is thread 2
wo yun xing le123
zhun bei shui 10 s

稍微复杂一点儿的 其实就是对 pthread_create()中的 attr 参数指定一些初始值,如手册中提到的
non-joinable RR priority 15
就是不可结合 RR调度策略 优先级为15

首先 创建attr变量 然后初始化

pthread_attr_t attr;
pthread_attr_init (&attr);

设置non-joinable属性

pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);

设置调度策略 和 优先级属性

pthread_attr_setinheritsched (&attr, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy (&attr, SCHED_RR);
attr.param.sched_priority = 15;

最后创建该thread

pthread_create (NULL, &attr, new_thread, NULL);

四、多线程的好处

可以快速计算 便于维护 反正 马路上的车道多了自然不会堵车 差不多也是那个道理吧

如果各个线程的运行结果对最终的输出结果没有啥影响的话,比如算个图像之类的一块一块拼起来就是了,那么就非常方便了 比如下面的伪代码:

int
main (int argc, char **argv)
{
int x1;
... // perform initializations
for (x1 = 0; x1 < num_x_lines; x1++) {
pthread_create (NULL, NULL, do_one_line, (void *) x1);
}
... // display results
}

在Neutrino中一个进程可以有32767个线程 你随便使 当然每个thread必须得有自己独立的stack 所以你还得有足够的stack 哈哈哈 不过我觉得一般也就够了

上述是单核CPU的情况,现在都是多核了,速度更快哇,就像下面的伪代码一样:

int num_lines_per_cpu;
int num_cpus;
int
main (int argc, char **argv)
{
int cpu;
... // perform initializations
// get the number of CPUs
num_cpus = _syspage_ptr -> num_cpu;
num_lines_per_cpu = num_x_lines / num_cpus;
for (cpu = 0; cpu < num_cpus; cpu++) {
pthread_create (NULL, NULL,
do_one_batch, (void *) cpu);
}
... // display results
}
void *
do_one_batch (void *c)
{
int cpu = (int) c;
int x1;
for (x1 = 0; x1 < num_lines_per_cpu; x1++) {
do_line_line (x1 + cpu * num_lines_per_cpu);
}
}

如果各个线程的计算结果对最终结果有影响的话,就像你一个公式中的参数一样,要所有参数都算完才能保证最后的结果是对的,那么怎么确定这些参数都计算完了呢,有两种解决方案:

  1. pthread_join()
int num_lines_per_cpu, num_cpus;
int main (int argc, char **argv)
{
int cpu;
pthread_t *thread_ids;
... // perform initializations
thread_ids = malloc (sizeof (pthread_t) * num_cpus);
num_lines_per_cpu = num_x_lines / num_cpus;
for (cpu = 0; cpu < num_cpus; cpu++) {
pthread_create (&thread_ids [cpu], NULL,
do_one_batch, (void *) cpu);
}
// synchronize to termination of all threads
for (cpu = 0; cpu < num_cpus; cpu++) {
pthread_join (thread_ids [cpu], NULL);
}
... // display results
}

就是在计算或者显示最终结果之前 加一个for循环 查看每个线程是否都 termination

  1. pthread_barrier_wait()

和join不同的是,在join时我们是等待所有thread结束。而barrier是等待一定数量的thread到达事先约定好的地方,等到了之后,unblock它们,它们会继续运行。下面是我在例程上稍作修改的代码,可以清楚的显示线程的运行

#include 
#include 
#include 
#include 
#include 

	 pthread_barrier_t barrier; // the barrier synchronization object
void *
thread1 (void *not_used)
{
	 time_t now;
	 char buf [27];
	 time (&now);
	 printf ("thread1 starting at %s", ctime_r (&now, buf));
	 // do the computation
	 // let’s just do a sleep here...
	 sleep (7);
	 time (&now);
	 printf ("thread1 () waiting for barrier at %s", ctime_r (&now, buf));
	 pthread_barrier_wait (&barrier);
	 // after this point, all three threads have completed
	time (&now);
	printf ("barrier in thread1() done at %s", ctime_r (&now, buf));
}
void *
thread2 (void *not_used)
{
	 time_t now;
	 char buf [27];
	 time (&now);
	 printf ("thread2 starting at %s", ctime_r (&now, buf));
	 // do the computation
	 // let’s just do a sleep here...
	 sleep (5);
	 time (&now);
	 printf ("thread2 () waiting for barrier at %s", ctime_r (&now, buf));
	 pthread_barrier_wait (&barrier);
	 // after this point, all three threads have completed.
	 time (&now);
	 printf ("barrier in thread2() done at %s", ctime_r (&now, buf));
	}
int main () // ignore arguments
{
	 time_t now;
	 char buf [27];
	 // create a barrier object with a count of 3
	 pthread_barrier_init (&barrier, NULL, 3);
	 // start up two threads, thread1 and thread2
	 pthread_create (NULL, NULL, thread1, NULL);
	 pthread_create (NULL, NULL, thread2, NULL);
	 sleep(2);
	 // at this point, thread1 and thread2 are running
	 // now wait for completion
	 time (&now);
	 printf ("main () waiting for barrier at %s", ctime_r (&now, buf));
	 pthread_barrier_wait (&barrier);
	 // after this point, all three threads have completed.
	 time (&now);
	 printf ("barrier in main () done at %s", ctime_r (&now, buf));
	 //sleep(2);
	 return EXIT_SUCCESS;
}

thread1 starting at Fri Jan 17 21:50:06 2020
thread2 starting at Fri Jan 17 21:50:06 2020
main () waiting for barrier at Fri Jan 17 21:50:08 2020
thread2 () waiting for barrier at Fri Jan 17 21:50:11 2020
thread1 () waiting for barrier at Fri Jan 17 21:50:13 2020
barrier in thread1() done at Fri Jan 17 21:50:13 2020
barrier in main () done at Fri Jan 17 21:50:13 2020

很意外的是
barrier in thread2() done at Fri Jan 17 21:50:13 2020 这句话被吞掉了,并没有才console中显示出来。然而将代码倒数第二行的sleep(2)延时打开后,就会正常显示,我猜这个吞掉的原因应该是main()线程比thread2()线程早挂掉导致的。。以至于thread1()为什么可以显示,你将thread2()的模拟计算时间设置的比1长的话,1就不会显示,而2会显示。所以还是不要让主线程过早的挂掉。。不然子线程就没啥用了

五、线程和进程的选择

你是选择一个process多个thread 还是 多个process 每个process有一个thread 这要取决于实际情况,前者的移植性好一点,而且支持网络传输或者工作。后者体积是最小的,速度也是最快的。具体要用哪个,还是要看实际情况再决定

你可能感兴趣的:(QNX学习笔记3 线程和进程 threads and processes)