Multithreaded execution where order of finished Work Items is preserved

后端 未结 9 1454
无人共我
无人共我 2021-02-01 08:28

I have a flow of units of work, lets call them \"Work Items\" that are processed sequentially (for now). I\'d like to speed up processing by doing the work multithreaded.

<
9条回答
  •  刺人心
    刺人心 (楼主)
    2021-02-01 09:11

    Just ID each of the objects for processing, create a proxy which would accept done work and allow to return it only when the ID pushed was sequential. A sample code below. Note how simple it is, utilizing an unsynchronized auto-sorting collection and just 2 simple methods as API.

    public class SequentialPushingProxy {
    
        static class OrderedJob implements Comparable{
            static AtomicInteger idSource = new AtomicInteger();
            int id;
    
            public OrderedJob() {
                id = idSource.incrementAndGet();
            }
    
            public int getId() {
                return id;
            }
    
            @Override
            public int compareTo(OrderedJob o) {
                return Integer.compare(id, o.getId());
            }
        }
    
        int lastId = OrderedJob.idSource.get();
    
        public Queue queue;
    
        public SequentialPushingProxy() {
            queue = new PriorityQueue();
        }
    
        public synchronized void pushResult(OrderedJob job) {
            queue.add(job);
        }
    
        List jobsToReturn = new ArrayList();
        public synchronized List getFinishedJobs() {
            while (queue.peek() != null) {
                // only one consumer at a time, will be safe
                if (queue.peek().getId() == lastId+1) {
                    jobsToReturn.add(queue.poll());
                    lastId++;
                } else {
                    break;
                }
            }
            if (jobsToReturn.size() != 0) {
                List toRet = jobsToReturn;
                jobsToReturn = new ArrayList();
                return toRet;
            }
            return Collections.emptyList();
        }
    
        public static void main(String[] args) {
            final SequentialPushingProxy proxy = new SequentialPushingProxy();
    
            int numProducerThreads = 5;
    
            for (int i=0; i ret = proxy.getFinishedJobs();
                            System.out.println("got "+ret.size()+" finished jobs");
                            try {
                                Thread.sleep(200);
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                        }
                    }
                }).start();
            }
    
    
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
            System.exit(0);
        }
    
    }
    

    This code could be easily improved to

    • allow pushing more than one job result at once, to reduce the synchronization costs
    • introduce a limit to returned collection to get done jobs in smaller chunks
    • extract an interface for those 2 public methods and switch implementations to perform tests

提交回复
热议问题