您的位置:首页 > 其它

【总结】进程,线程,并发

2012-04-28 19:49 204 查看

进程,线程,并发

文/xichen 2011年3月

写这个文档是希望通过,概念的理解和程序的测试认识“进程”“线程”“并发等概念”。关于基本概念不想写太多,操作系统每本书里面都会重点讲的,看了一些资料,有一些自己的理解。

进程是表示资源分配的基本单位,它运行起来需要的是系统资源,是系统给他分配的一些资源。至于是不是系统调度的基本单位,实际上跟操作系统内核是有关系的,在Mac、Windows NT等采用微内核结构的操作系统中,进程的功能发生了变化:它只是资源分配的单位,而不再是调度运行的单位。在微内核系统中,真正调度运行的基本单位是线程。因此,实现并发功能的单位是线程。有必要说下,进程需要的资源和线程需要的资源。跟操作系统有关,但是也很相像。

其实对于我本人而言我是很希望知道在操作系统内部究竟是怎么给进程和线程分配资源的:

进程:查到了一个很好的关于linux0.11中的进程的数据结构
http://www.cppblog.com/jake1036/archive/2010/11/13/133530.html
线程:一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。

线程自己的资源:栈,私有数据,但是多个线程是共享内存单元的,(是有一定的安全问题的)。

关于线程的数据结构:http://www.programfan.com/blog/article.asp?id=44349

操作系统创建线程实际上就是在处理器调度的时候能够在最小的资源代价下切换任务。一个说法是:进程切换比线程切换开销大是因为进程切换时要切页表,而且往往伴随着页调度,因为进程的数据段代码段要换出去,以便把将要执行的进程的内容换进来。本来进程的内容就是线程的超集。而且线程只需要保存线程的上下文(相关寄存器状态和栈的信息)就好了,动作很小。在多处理器下,又允许几个线程各自在单独处理上进行,这样能够很好的实现这种并发性。

关于并发,这里给出一个我见过的最好的一个定义:如果逻辑控制流在时间上重叠,那么它们就是并发。PS:逻辑控制流:当我们在使用调试器单步地执行我们的程序时,会看到一系列的PC(程序计数器)的值,这些值唯一地对应于包含在我们程序的可执行目标文件中的指令或者是包含在运行时动态链接到我们的共享对象中的指令,这个PC值的序列就叫做逻辑控制流。

实际上,很多时候并发性是一种内核用来运行多个应用程序的策略,但是并不局限于内核。应用级并发的应用程序:分别是基于进程的,I/O多路复用,线程的并发。

进程并发和线程并发有很多区别,具体有一个很好的对比参考http://programmerdigest.cn/2010/08/1096.html

多线程的编程主要涉及到进程的控制和进程通信(IPC),如果写比较小的代码都要考虑进程同步或者其他神马问题,后面提到的代码都是些多线程的。原理倒是一样的。

关于多线程的创建是会遇到下面的一个问题的:

#include<windows.h>

#include<stdio.h>

#define MAX_THREADS 5

typedef struct _THREAD_PARAM

{

DWORD i;

DWORD dwRandom;

DWORD dwData;

}THREAD_PARAM,*LPTHREAD_PARAM;

DWORD WINAPI ThreadProc( LPVOID lpParam)

{

LPTHREAD_PARAM pData;

pData = (LPTHREAD_PARAM)lpParam;

printf("TID=%u,\t parameters = %u,%u,%u\n",

GetCurrentThreadId(),

pData->i,pData->dwRandom,pData->dwData);

HeapFree(GetProcessHeap(),0,pData);

return 0;

}

int main()

{

LPTHREAD_PARAM pData;

DWORD dwThreadId[MAX_THREADS];

HANDLE hThread[MAX_THREADS];

int i;

for(i=0;i<MAX_THREADS;i++)

{

pData=(LPTHREAD_PARAM)HeapAlloc(GetProcessHeap(),

HEAP_ZERO_MEMORY,

sizeof(THREAD_PARAM));

if (pData == NULL)

{

printf("heapallc error;\n");

ExitProcess(2);

}

pData->i= i;

pData->dwRandom=rand();

pData->dwData=100;

hThread[i]=CreateThread(NULL,0,ThreadProc,pData,0,&dwThreadId[i]);

/*

解决办法:

//等待所有线程结束

WaitForMultipleObjects(MAX_THREADS,hThread,TRUE,INFINITE);

for(i=0;i<MAX_THREADS;i++)

{

CloseHandle(hThread[i]);

}

*/

if(hThread[i]==NULL)

{

ExitProcess(i);

}

}

return 0;

}

编译运行以上程序,目的是创建5个线程,但是实际上,运行之后在我的机器上只能够得到2个线程,在其他机器上能够出现4个线程,这是因为有的线程还没执行完,主线程就退出了,这个说法我觉得是很肤浅的,没有更多的解释,具体的解决办法也是比较简单,就是等待线程全部完成再退出。

但是对此我有疑问:

1.这个程序能够运行几个线程跟什么有关?

2.主线程究竟能够运行多久?极端的去想,创建一个线程,创建1W个线程会有哪些具体的不同?

…………………..

…………………..

之前看过但是没写过的,一个linux多线程例子,在原来的基础上写了下,GCC下编译通过了,上代码:

#include <pthread.h>

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <string.h>

#define TN 3 /*numbers of threads*/

void routine(void * arg);

int main()

{

pthread_t thread[TN];

int retval,i;

char msg[TN][10];

puts("this is main thread!.");

for(i = 0;i<TN;i++)

{

sprintf(msg[i],"thread %d",i);

retval = Pthread_create(&thread[i],NULL,(void *)routine,msg[i]);

if(retval!= 0)

{

perror("thread create failed.");

exit(1);

}

}

for(i = 0;i<TN;i++)

Pthread_join(thread[i],NULL);

puts("main thread exiting.");

return 0;

}

void routine(void * arg)

{

int j;

for(j=0 ; j<5;j++)

{

sleep(1+(int)(5.0*rand()/(RAND_MAX+1.0)));/*睡眠时间,通过随机数进行控制*/

printf("%s:",(char *)arg);

printf("% d\n",j);

}

return;

}

以上的代码在linux中使用GCC运行,再运行之后,得到了不同的结果,体现了多线程的并发执行……

类似的我在WINDOWS下面也改写了这个程序,上代码:

#include <process.h>

#include <stdio.h>

#include <windows.h>

void thread_proc(void *arg)

{

printf("a thread %d,id is:",arg);

_endthread();

}

int main()

{

unsigned pid;

int count=0;

while(count<5)//线程数

{

_beginthreadex(NULL,0, (unsigned (__stdcall *) (void *))thread_proc,(void *)count,0,&pid);//创建线程

Sleep(1+(int)(10.0*rand()/(RAND_MAX+1.0)));

//Sleep( 10);

/*如果你选择一个固定时间,比如10秒钟,也是可以的,但是并不能很好地确定该数值的大小,

但是我不知道酱紫说对还是不对,我觉得这个可能跟操作系统的那个调度算法有关,比如时间片调度的话,据说一个时间片可以 调度10个进程。后面提到,肯定还是要写程序测试一下地……

*/

printf("%d\n",pid);

count++;

}

return 0;

}

/*关键函数

uintptr_t _beginthreadex(

void *security,

unsigned stack_size,

unsigned ( *start_address )( void * ),

void *arglist,

unsigned initflag,

unsigned *thrdaddr

)

其实…… _endthread()也很关键,往往收尾的好人总是被大家遗忘……

*/

实际上又一次查阅了创建线程的函数的区别,给出一个好的链接:

http://hi.baidu.com/kings0527/blog/item/11838d02a1b3bbe608fa93ff.html

很多时候的很多结果,不一定如我们所想,虽然仔细分析可以得到正确的结果,但是,但是悲剧的是,很多时候我们喜欢想当然

代码,一个很好的关于竞争的例子:

#include"csapp.h"

#define N 4

void *thread(void *vargp);

int main()

{

pthread_t tid
;

int i;

for(i = 0; i<N; i++)

Pthread_create(&tid[i],NULL,thread,&i);

for(i = 0; i<N; i++)

Pthread_join(tid[i],NULL);

exit(0);

}

void *thread(void *vargp)

{

int myid = *((int *)vargp);

printf("this is from thread %d",myid);

return NULL;

}

运行结果并不是我们预想的:0123 是其他的序列如:1323
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐