精华内容
下载资源
问答
  • 线程fork进程

    千次阅读 2013-10-15 22:03:10
    一个多线程进程的某个线程fork出一个子进程,那么子进程完整复制调用fork的线程,且子进程不会拥有原进程那么多的线程,这里出现一个潜在的死锁条件,就是子进程继承了父进程的互斥锁但是并不知道该互斥锁的状态,若...

     一个多线程进程的某个线程fork出一个子进程,那么子进程完整复制调用fork的线程,且子进程不会拥有原进程那么多的线程,这里出现一个潜在的死锁条件,就是子进程继承了父进程的互斥锁但是并不知道该互斥锁的状态,若该互斥锁以上锁且子进程再次加锁该互斥量,此时子进程将死锁。

    #include <pthread.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <wait.h>
    
    pthread_mutex_t mutex;
    
    void* another( void* arg )
    {
        printf( "in child thread, lock the mutex\n" );
        pthread_mutex_lock( &mutex );
        sleep( 5 );
        pthread_mutex_unlock( &mutex );
    }
    
    void prepare()
    {
        pthread_mutex_lock( &mutex );
    }
    
    void infork()
    {
        pthread_mutex_unlock( &mutex );
    }
    
    int main()
    {
        pthread_mutex_init( &mutex, NULL );
        pthread_t id;
        pthread_create( &id, NULL, another, NULL );
        //pthread_atfork( prepare, infork, infork );//pthread_atfork()可以确保fork后父进程和子进程都拥有一个清楚的锁状态,prepare将父进程所有互斥锁锁住,infork释放prepare锁住的互斥锁,调用这行时子进程就不会死锁了
        sleep( 1 );
        int pid = fork();
        if( pid < 0 )
        {
            pthread_join( id, NULL );
            pthread_mutex_destroy( &mutex );
            return 1;
        }
        else if( pid == 0 )
        {
            printf( "I anm in the child, want to get the lock\n" );
            pthread_mutex_lock( &mutex );
            printf( "I can not run to here, oop...\n" );
            pthread_mutex_unlock( &mutex );
            exit( 0 );
        }
        else
        {
            pthread_mutex_unlock( &mutex );
            wait( NULL );
        }
        pthread_join( id, NULL );
        pthread_mutex_destroy( &mutex );
        return 0;
    }


    展开全文
  • linux 进程 线程 fork 的深入思考 一道面试题的思考
  • 线程 fork

    2012-04-26 09:52:21
    线程 fork 继续前几天的话题。做梦幻西游服务器优化的事情。以往的代码,定期存盘的工作分两个步骤,把 VM 里的动态数据序列化,然后把序列化后的数据写盘。这两个步骤,序列化工作并没有独立在单独线程/进程...
    原帖:http://hi.baidu.com/skw0rm/blog/item/ca04a0f039950a8fa40f52ab.html
    多线程 fork


    继续前几天的话题。做梦幻西游服务器优化的事情。以往的代码,定期存盘的工作分两个步骤,把 VM 里的动态数据序列化,然后把序列化后的数据写盘。这两个步骤,序列化工作并没有独立在单独线程/进程里做,而是放在主线程的。IO 部分则在一个独立进程中。
    序列化任务是个繁琐的过程。非常耗时(相对于 MMORPG 这个需要对用户请求快速反应的环境)。当玩家同时在线人数升高时,一个简便的优化方法是把整个序列化任务分步完成,分摊到多个心跳内。这里虽然有一些数据一致性问题,但也有不同的手段解决。
    但是,在线人数达到一定后,序列化过程依然会对系统性能造成较大影响。在做定期存盘时,玩家的输入反应速度明显变大。表现得是游戏服务器周期性的卡。为了缓解这一点,我希望改造系统,把序列化任务分离到独立进程去做。
    方法倒是很简单,在定期存盘一刻,调用 fork ,然后在子进程中慢慢的做序列化工作。(可以考虑使用 nice)做完后,再把数据交到 IO 进程写盘。不过鉴于我们前期设计的问题,具体实现中,我需要通过共享内存把序列化结果交还父进程,由父进程送去 IO 进程。
    因为 fork 会产生一个内存快照,所以甚至没有数据一致性问题。这应该是一个网络游戏用到的常见模式。
    可问题就出在于,经过历史变迁,我们的服务器已经使用了多线程,这使得 fork 子进程的做法变的不那么可靠,需要自己推敲一下。
    多进程的多线程程序,听起来多不靠谱。真是闲得淡疼的人才会做此设计。但依旧可以使用万能的推辞:历史造成的。
    在 POSIX 标准中,fork 的行为是这样的:复制整个用户空间的数据(通常使用 copy-on-write 的策略,所以可以实现的速度很快)以及所有系统对象,然后仅复制当前线程到子进程。这里:所有父进程中别的线程,到了子进程中都是突然蒸发掉的。
    其它线程的突然消失,是一切问题的根源。
    我之前从未写过多进程多线程程序,不过公司里有 David Xu 同学(他实现维护着 FreeBSD 的线程库)是这方面的专家,今天跟徐同学讨论了一下午,终于觉得自己搞明白了其中的纠结。嗯,写点东西整理一下思路。
    可能产生的最严重的问题是锁的问题。
    因为为了性能,大部分系统的锁是实现在用户空间的。所以锁对象会因为 fork 复制到子进程中。
    对于锁来说,从 OS 看,每个锁有一个所有者,即最后一次 lock 它的线程。
    假设这么一个环境,在 fork 之前,有一个子线程 lock 了某个锁,获得了对锁的所有权。fork 以后,在子进程中,所有的额外线程都人间蒸发了。而锁却被正常复制了,在子进程看来,这个锁没有主人,所以没有任何人可以对它解锁。
    当子进程想 lock 这个锁时,不再有任何手段可以解开了。程序发生死锁。
    为何,POSIX 指定标准时,会定下这么一个显然不靠谱的规则?允许复制一个完全死掉的锁?答案是历史和性能。因为历史上,把锁实现在用户态是最方便的(今天依旧如此)。背后可能只需要一条原子操作指令即可。大多数 CPU 都支持的。fork 只管用户空间的复制,不会涉及其中的对象细节。
    一般的惯例,多线程程序 fork 前,应该由发起 fork 的线程 lock 所有子进程可能用到的锁,fork 后,把它们一一 unlock 。当然,这样的做法就隐含了锁的次序。如果次序和平时不同,那么就会死锁。
    不光是显式的使用锁,许多 CRT 函数也会间接的使用。比如 fprintf 这些文件操作。因为对 FILE * 的操作是依靠锁来达到线程安全的。最常见的问题是在子线程里调用 fprintf 写 log 。
    除此之外,就是要小心一些不依赖锁的数据一致性问题了。比如若在父进程里另一个线程中操作一个链表,fork 发生时,因为其它线程的突然消失,这个链表就可能会因为只操作了一半而是不完整的数据。不过这一般不会是问题,或者可以归咎于对锁的处理。(多个线程,访问同一块数据。比如一条链表。就是需要加锁的)
    展开全文
  • java中线程fork join

    2018-07-31 15:14:49
    1. 什么是Fork/Join框架 Fork/Join框架是Java7提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。 我们再通过Fork和Join这两个单词来理解...

    1. 什么是Fork/Join框架

    Fork/Join框架是Java7提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。

    我们再通过Fork和Join这两个单词来理解下Fork/Join框架,Fork就是把一个大任务切分为若干子任务并行的执行,Join就是合并这些子任务的执行结果,最后得到这个大任务的结果。比如计算1+2+。。+10000,可以分割成10个子任务,每个子任务分别对1000个数进行求和,最终汇总这10个子任务的结果。

    工作窃取算法

    工作窃取(work-stealing)算法是指某个线程从其他队列里窃取任务来执行。工作窃取的运行流程图如下:

    fj

    那么为什么需要使用工作窃取算法呢?假如我们需要做一个比较大的任务,我们可以把这个任务分割为若干互不依赖的子任务,为了减少线程间的竞争,于是把这些子任务分别放到不同的队列里,并为每个队列创建一个单独的线程来执行队列里的任务,线程和队列一一对应,比如A线程负责处理A队列里的任务。但是有的线程会先把自己队列里的任务干完,而其他线程对应的队列里还有任务等待处理。干完活的线程与其等着,不如去帮其他线程干活,于是它就去其他线程的队列里窃取一个任务来执行。而在这时它们会访问同一个队列,所以为了减少窃取任务线程和被窃取任务线程之间的竞争,通常会使用双端队列,被窃取任务线程永远从双端队列的头部拿任务执行,而窃取任务的线程永远从双端队列的尾部拿任务执行。

    工作窃取算法的优点是充分利用线程进行并行计算,并减少了线程间的竞争,其缺点是在某些情况下还是存在竞争,比如双端队列里只有一个任务时。并且消耗了更多的系统资源,比如创建多个线程和多个双端队列。

    3. Fork/Join框架的介绍

    我们已经很清楚Fork/Join框架的需求了,那么我们可以思考一下,如果让我们来设计一个Fork/Join框架,该如何设计?这个思考有助于你理解Fork/Join框架的设计。

    第一步分割任务。首先我们需要有一个fork类来把大任务分割成子任务,有可能子任务还是很大,所以还需要不停的分割,直到分割出的子任务足够小。

    第二步执行任务并合并结果。分割的子任务分别放在双端队列里,然后几个启动线程分别从双端队列里获取任务执行。子任务执行完的结果都统一放在一个队列里,启动一个线程从队列里拿数据,然后合并这些数据。

    Fork/Join使用两个类来完成以上两件事情:

    • ForkJoinTask:我们要使用ForkJoin框架,必须首先创建一个ForkJoin任务。它提供在任务中执行fork()和join()操作的机制,通常情况下我们不需要直接继承ForkJoinTask类,而只需要继承它的子类,Fork/Join框架提供了以下两个子类:
      • RecursiveAction:用于没有返回结果的任务。
      • RecursiveTask :用于有返回结果的任务。
    • ForkJoinPool :ForkJoinTask需要通过ForkJoinPool来执行,任务分割出的子任务会添加到当前工作线程所维护的双端队列中,进入队列的头部。当一个工作线程的队列里暂时没有任务时,它会随机从其他工作线程的队列的尾部获取一个任务。

    4. 使用Fork/Join框架

    让我们通过一个简单的需求来使用下Fork/Join框架,需求是:计算1+2+3+4的结果。

    使用Fork/Join框架首先要考虑到的是如何分割任务,如果我们希望每个子任务最多执行两个数的相加,那么我们设置分割的阈值是2,由于是4个数字相加,所以Fork/Join框架会把这个任务fork成两个子任务,子任务一负责计算1+2,子任务二负责计算3+4,然后再join两个子任务的结果。

    列子

    import java.util.concurrent.ForkJoinPool;
    import java.util.concurrent.Future;
    import java.util.concurrent.RecursiveTask;
    
    public class CountTask extends RecursiveTask<Integer>
    {
        private static final long serialVersionUID = -3611254198265061729L;
        
        public static final int threshold = 2;
        private int start;
        private int end;
        
        public CountTask(int start, int end)
        {
            this.start = start;
            this.end = end;
        }
    
        @Override
        protected Integer compute()
        {
            int sum = 0;
            
            //如果任务足够小就计算任务
            boolean canCompute = (end - start) <= threshold;
            if(canCompute)
            {
                for (int i=start; i<=end; i++)
                {
                    sum += i;
                }
            }
            else
            {
                // 如果任务大于阈值,就分裂成两个子任务计算
                int middle = (start + end)/2;
                CountTask leftTask = new CountTask(start, middle);
                CountTask rightTask = new CountTask(middle+1, end);
                System.out.println(Thread.currentThread().getName());
                // 执行子任务
               // leftTask.fork();
                //rightTask.fork();
                invokeAll(leftTask,rightTask);
                //等待任务执行结束合并其结果
                int leftResult = leftTask.join();
                int rightResult = rightTask.join();
                
                //合并子任务
                sum = leftResult + rightResult;
        
            }
            
            return sum;
        }
        
        public static void main(String[] args)
        {
            ForkJoinPool forkjoinPool = new ForkJoinPool();
            
            //生成一个计算任务,计算1+2+3+4
            CountTask task = new CountTask(1, 100);
            
            //执行一个任务
            Future<Integer> result = forkjoinPool.submit(task);
            
            try
            {
                System.out.println(result.get());
            }
            catch(Exception e)
            {
                System.out.println(e);
            }
        }
    
    }

     

     

    展开全文
  • Fork-Join多线程并发框架 ——JDK7提供的一种新的并发框架:分解、合并 基于分治递归的思想 ——适用于整体任务量不确定,但单个最小任务确定的情况使用     ForkJoin并发框架的主要类包括: ForkJoinPool...

    Fork-Join多线程并发框架

    ——JDK7提供的一种新的并发框架:分解、合并  基于分治递归的思想

    ——适用于整体任务量不确定,但单个最小任务确定的情况使用

     

     

    ForkJoin并发框架的主要类包括:

    ForkJoinPool:ForkJoin线程池,实现了ExecutorService接口和工作窃取算法,用于线程调度与管理。


    ForkJoinTask:ForkJoin任务,提供了fork()方法和join()方法。通常不直接使用,而是使用以下子类: 

     

    • RecursiveAction:无返回值的任务,通常用于只fork不join的情形

    • RecursiveTask:有返回值的任务,通常用于fork+join的情形
    •  

     

     

    递归任务类SumTask

    ——继承RecursiveTask递归父类 重写compute()计算方法

    ——若计算区间小于一定值 那么计算返回

    ——否则 递归分解成更小的任务

    ——调用invokeAll()方法执行  结果采用Join方法获取

    ——将结果相加返回

    //继承 Recurisetask 递归任务父类
    public class SumTask  extends RecursiveTask<Long>{
    	
    	private int start;
    	private int end;
    	public SumTask(int start,int end)
    	{
    		this.start=start;
    		this.end=end;
    	}
    	//static final变量永远不变 编译器就确定
    	public static final int threadhold=5;//最小任务区间长度
    	@Override
    	protected Long compute()
    	{
    		Long sum=0L;
    		//任务小于给定值 计算
    		boolean canCompute=(end-start)<=threadhold;
    		if(canCompute)
    		{
    			for(int i=start;i<=end;i++)
    				sum+=i;
    		}
    		else
    		{
    			int middle=(start+end)/2;
    			SumTask subTask1=new SumTask(start,middle);
    			SumTask subTask2=new SumTask(middle+1,end);
    			//递归计算
    			invokeAll(subTask1,subTask2);
    			Long sum1=subTask1.join();
    			Long sum2=subTask2.join();
    			sum=sum1+sum2;
    			
    		}
    		return sum;
    	}
    
    }

    主线程

    ——其中forkJoinPool.submit提交任务(传入RecursiveTask对象)

    ——ForkJoinPool.getParallelism获取并行的线程数目

    (注意 并发(Concurrency). VS 并行(Parallelism).)

    ——

    并发

    偏重于多个任务交替执行,而多个任务之间有可能还是串行的。

    并行

    真正意义上的“同时执行”。

     

    public class SumTest {
    
    	public static void main(String[] args) throws ExecutionException,InterruptedException{
    		// TODO Auto-generated method stub
    		//创建线程池
    		ForkJoinPool pool=new ForkJoinPool();
    		//创建任务
    		SumTask task=new SumTask(1,100000000);
    		
    		//提交任务
    		ForkJoinTask<Long> result=pool.submit(task);
    		//等待结果
    		do
    		{
    			System.out.printf("Main:Thread count %d \n", pool.getActiveThreadCount());
    			System.out.printf("Main Paralelism %d \n", pool.getParallelism());
    			try
    			{
    				Thread.sleep(50);
    			}catch(InterruptedException e)
    			{
    				e.printStackTrace();
    			}
    		}while(!task.isDone());
    		//输出结果
    		System.out.println(result.get().toString());
    
    	}
    
    }
    

    执行结果:

     

     

     

    展开全文
  • 线程fork的安全性

    2009-09-22 18:45:00
    进程创建中的 fork 问题Solaris 9 产品和更早 Solaris 发行版中处理 fork() 的缺省方式与在 POSIX 线程中处理 fork() 的方式稍有不同。 对于 Solaris 9 之后的 Solaris 发行版,在所有情况下,fork() 都会按照为 ...
  • glibc的fprintf()线程安全实现吗? 多线程内同时写一个文件安全,但是多进程不安全。 多线程内:有隐式的锁。...但是,在多线程中用fork,其他线程蒸发掉,锁在新进程中是无法解开,导致如果在次加锁,产生死锁。 ...
  • Python多线程fork

    2017-11-21 18:45:19
    Python 多线程 fork
  • 线程fork

    千次阅读 2014-02-27 01:08:56
     当线程调用fork时,就为子进程创建了整个进程地址空间的副本,父子进程通过写时复制技术来共享内存页的这一副本。  子进程通过几成整个地址空间的副本,也从父进程那里继承了所有互斥量、读写锁和条件变量的状态...
  • 线程fork

    2016-02-23 00:08:04
    如果父进程是多线程进程,那么进行父进程进行fork后,会发生什么?子进程是否也变成多线程进程? 真相是:在子进程内部只存在一个线程,它是由父进程中调用fork线程的副本构成的。 回顾fork编程,子进程会继承...
  • 4. 线程安全 5. 线程fork
  • 102-多线程fork

    千次阅读 2017-03-17 20:26:41
    在多线程程序中使用 fork,可能会导致一些意外: 子进程中只剩下一个线程,它是父进程中调用 fork线程的副本构成。这意味着在多线程环境中,会导致“线程蒸发”,莫名奇妙的失踪! 因为线程蒸发,它们所持有的锁...
  • linux线程fork

    2019-04-25 10:24:57
    线程调用fork函数时,就为子进程创建了整个进程地址空间的副本,子进程通过继承整个地址空间的副本,也会将父进程的互斥里、读写锁、条件变里的状态继承过来。也就是说,如果父进程中互斥里是锁着的,那么在子进程...
  •  Linux操作系统实现线程的机制比较的特殊,从内核的角度来讲,Linux上把所有的线程都当做进程来实现,内核中没有特别的准备调度算法和数据结构来表征线程,相反,线程仅仅被视为一个与其他进程共享某些资源的进程,每个...
  • 线程fork-

    2017-10-22 23:12:13
    关键接口: pthread_atfork(void (*prepare)(void),void (*parent)(void), void(*child)(void))重点内容 pthread_atfork: 最先调用....child: fork返回之前,在子进程环境中调用,在这里unlock prepare
  • 线程fork函数的使用

    千次阅读 2017-08-11 22:10:12
    线程fork函数的是使用(线程与进程的结合) 线程中调用fork函数创建子进程,子进程仅仅执行调用fork函数的这个线程,其他的线程不会被调用。
  • 线程fork 对于fork相信大家都已经熟悉的不能再熟悉了! 我们最近学习到了线程,我们都知道了新创建的线程可以访问进程的地址空间,并且继承调用线程 的浮点环境和信号屏蔽字.线程中都可以访问到进程的...
  • 1.多线程中某个线程调用 fork(),子进程会有和父进程相同数量的线程吗? 1.1fork在创建线程前 pthread_fork1.c代码: #include<stdio.h> #include<stdlib.h> #include<string.h> #include<...
  • 线程fork

    2014-04-10 15:34:30
    线程程序与fork() 多线程程序里不准使用fork UNIX上C++程序设计守则3 准则3:多线程程序里不准使用fork 在多线程程序里,在”自身以外的线程存在的状态”下一使用fork的话,就可能引起各种各样的...
  • [APUE] 线程fork

    2014-04-17 20:14:44
    线程fork
  • 不会,只会复制当前的线程 参考: https://www.cnblogs.com/liyuan989/p/4279210.html
  • 线程的父进程调用 fork 函数创建子进程时,
  • 当多线程进程调用fork创建子进程时,Pthreads指定只有那个调用fork线程在子进程内存在(表示子进程中只有调用线程这个线程)。尽管当从fork调用返回时,只有调用线程在子进程中存在,所有其他的Pthreads线程状态仍...
  • 线程中有一个线程调用fork,创建的子进程中会有几个线程? 创建的子进程中,只有调用fork线程被启动,其他线程并没有执行。 实例: 多线程创建子进程,并且在创建之前有对互斥锁加锁,子进程中锁的状态如何? ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 90,612
精华内容 36,244
关键字:

线程fork