python有线程池吗_python——有一种线程池叫做自己写的线程池

1 #-*- coding: UTF-8 -*-

2 """Easy to use object-oriented thread pool framework.3

4 A thread pool is an object that maintains a pool of worker threads to perform5 time consuming operations in parallel. It assigns jobs to the threads6 by putting them in a work request queue, where they are picked up by the7 next available thread. This then performs the requested operation in the8 background and puts the results in another queue.9

10 The thread pool object can then collect the results from all threads from11 this queue as soon as they become available or after all threads have12 finished their work. It's also possible, to define callbacks to handle13 each result as it comes in.14

15 The basic concept and some code was taken from the book "Python in a Nutshell,16 2nd edition" by Alex Martelli, O'Reilly 2006, ISBN 0-596-10046-9, from section17 14.5 "Threaded Program Architecture". I wrapped the main program logic in the18 ThreadPool class, added the WorkRequest class and the callback system and19 tweaked the code here and there. Kudos also to Florent Aide for the exception20 handling mechanism.21

22 Basic usage::23

24 >>> pool = ThreadPool(poolsize)25 >>> requests = makeRequests(some_callable, list_of_args, callback)26 >>> [pool.putRequest(req) for req in requests]27 >>> pool.wait()28

29 See the end of the module code for a brief, annotated usage example.30

31 Website : http://chrisarndt.de/projects/threadpool/32

33 """

34 __docformat__ = "restructuredtext en"

35

36 __all__ =[37 'makeRequests',38 'NoResultsPending',39 'NoWorkersAvailable',40 'ThreadPool',41 'WorkRequest',42 'WorkerThread'

43 ]44

45 __author__ = "Christopher Arndt"

46 __version__ = '1.3.2'

47 __license__ = "MIT license"

48

49

50 #standard library modules

51 importsys52 importthreading53 importtraceback54

55 try:56 import Queue #Python 2

57 exceptImportError:58 import queue as Queue #Python 3

59

60

61 #exceptions

62 classNoResultsPending(Exception):63 """All work requests have been processed."""

64 pass

65

66 classNoWorkersAvailable(Exception):67 """No worker threads available to process remaining requests."""

68 pass

69

70

71 #internal module helper functions

72 def_handle_thread_exception(request, exc_info):73 """Default exception handler callback function.74

75 This just prints the exception info via ``traceback.print_exception``.76

77 """

78 traceback.print_exception(*exc_info)79

80

81 #utility functions

82 def makeRequests(callable_, args_list, callback=None,83 exc_callback=_handle_thread_exception):84 """Create several work requests for same callable with different arguments.85

86 Convenience function for creating several work requests for the same87 callable where each invocation of the callable receives different values88 for its arguments.89

90 ``args_list`` contains the parameters for each invocation of callable.91 Each item in ``args_list`` should be either a 2-item tuple of the list of92 positional arguments and a dictionary of keyword arguments or a single,93 non-tuple argument.94

95 See docstring for ``WorkRequest`` for info on ``callback`` and96 ``exc_callback``.97

98 """

99 requests =[]100 for item inargs_list:101 ifisinstance(item, tuple):102 requests.append(103 WorkRequest(callable_, item[0], item[1], callback=callback,104 exc_callback=exc_callback)105 )106 else:107 requests.append(108 WorkRequest(callable_, [item], None, callback=callback,109 exc_callback=exc_callback)110 )111 returnrequests112

113

114 #classes

115 classWorkerThread(threading.Thread):116 """Background thread connected to the requests/results queues.117

118 A worker thread sits in the background and picks up work requests from119 one queue and puts the results in another until it is dismissed.120

121 """

122

123 def __init__(self, requests_queue, results_queue, poll_timeout=5, **kwds):124 """Set up thread in daemonic mode and start it immediatedly.125

126 ``requests_queue`` and ``results_queue`` are instances of127 ``Queue.Queue`` passed by the ``ThreadPool`` class when it creates a128 new worker thread.129

130 """

131 threading.Thread.__init__(self, **kwds)132 self.setDaemon(1)133 self._requests_queue =requests_queue134 self._results_queue =results_queue135 self._poll_timeout =poll_timeout136 self._dismissed =threading.Event()137 self.start()138

139 defrun(self):140 """Repeatedly process the job queue until told to exit."""

141 whileTrue:142 ifself._dismissed.isSet():143 #we are dismissed, break out of loop

144 break

145 #get next work request. If we don't get a new request from the

146 #queue after self._poll_timout seconds, we jump to the start of

147 #the while loop again, to give the thread a chance to exit.

148 try:149 request =self._requests_queue.get(True, self._poll_timeout)150 exceptQueue.Empty:151 continue

152 else:153 ifself._dismissed.isSet():154

155 #we are dismissed, put back request in queue and exit loop

156 self._requests_queue.put(request)157 break

158 try:159 result = request.callable(*request.args, **request.kwds)160 self._results_queue.put((request, result))161 except:162 request.exception =True163 self._results_queue.put((request, sys.exc_info()))164

165 defdismiss(self):166 print '**********dismiss***********'

167 """Sets a flag to tell the thread to exit when done with current job.168 """

169 self._dismissed.set()170

171

172 classWorkRequest:173 """A request to execute a callable for putting in the request queue later.174

175 See the module function ``makeRequests`` for the common case176 where you want to build several ``WorkRequest`` objects for the same177 callable but with different arguments for each call.178

179 """

180

181 def __init__(self, callable_, args=None, kwds=None, requestID=None,182 callback=None, exc_callback=_handle_thread_exception):183 """Create a work request for a callable and attach callbacks.184

185 A work request consists of the a callable to be executed by a186 worker thread, a list of positional arguments, a dictionary187 of keyword arguments.188

189 A ``callback`` function can be specified, that is called when the190 results of the request are picked up from the result queue. It must191 accept two anonymous arguments, the ``WorkRequest`` object and the192 results of the callable, in that order. If you want to pass additional193 information to the callback, just stick it on the request object.194

195 You can also give custom callback for when an exception occurs with196 the ``exc_callback`` keyword parameter. It should also accept two197 anonymous arguments, the ``WorkRequest`` and a tuple with the exception198 details as returned by ``sys.exc_info()``. The default implementation199 of this callback just prints the exception info via200 ``traceback.print_exception``. If you want no exception handler201 callback, just pass in ``None``.202

203 ``requestID``, if given, must be hashable since it is used by204 ``ThreadPool`` object to store the results of that work request in a205 dictionary. It defaults to the return value of ``id(self)``.206

207 """

208 #__init__( callable_, args=None, kwds=None, callback=None, exc_callback=_handle_thread_exception)

209 #WorkRequest(callable_, item[0], item[1], callback=callback,exc_callback=exc_callback)

210 if requestID isNone:211 self.requestID =id(self)212 else:213 try:214 self.requestID =hash(requestID)215 exceptTypeError:216 raise TypeError("requestID must be hashable.")217 self.exception =False218 self.callback =callback219 self.exc_callback =exc_callback220 self.callable =callable_221 self.args = args or[]222 self.kwds = kwds or{}223

224 def __str__(self):225 return "" %\226 (self.requestID, self.args, self.kwds, self.exception)227

228 classThreadPool:229 """A thread pool, distributing work requests and collecting results.230

231 See the module docstring for more information.232

233 """

234

235 def __init__(self, num_workers, q_size=0, resq_size=0, poll_timeout=5):236 """Set up the thread pool and start num_workers worker threads.237

238 ``num_workers`` is the number of worker threads to start initially.239

240 If ``q_size > 0`` the size of the work *request queue* is limited and241 the thread pool blocks when the queue is full and it tries to put242 more work requests in it (see ``putRequest`` method), unless you also243 use a positive ``timeout`` value for ``putRequest``.244

245 If ``resq_size > 0`` the size of the *results queue* is limited and the246 worker threads will block when the queue is full and they try to put247 new results in it.248

249 .. warning:250 If you set both ``q_size`` and ``resq_size`` to ``!= 0`` there is251 the possibilty of a deadlock, when the results queue is not pulled252 regularly and too many jobs are put in the work requests queue.253 To prevent this, always set ``timeout > 0`` when calling254 ``ThreadPool.putRequest()`` and catch ``Queue.Full`` exceptions.255

256 """

257 self._requests_queue =Queue.Queue(q_size)258 self._results_queue =Queue.Queue(resq_size)259 self.workers =[]260 self.dismissedWorkers =[]261 self.workRequests ={}262 self.createWorkers(num_workers, poll_timeout)263

264 def createWorkers(self, num_workers, poll_timeout=5):265 """Add num_workers worker threads to the pool.266

267 ``poll_timout`` sets the interval in seconds (int or float) for how268 ofte threads should check whether they are dismissed, while waiting for269 requests.270

271 """

272 for i inrange(num_workers):273 self.workers.append(WorkerThread(self._requests_queue,274 self._results_queue, poll_timeout=poll_timeout))275

276 def dismissWorkers(self, num_workers, do_join=False):277 """Tell num_workers worker threads to quit after their current task."""

278 dismiss_list =[]279 for i inrange(min(num_workers, len(self.workers))):280 worker =self.workers.pop()281 worker.dismiss()282 dismiss_list.append(worker)283

284 ifdo_join:285 for worker indismiss_list:286 worker.join()287 else:288 self.dismissedWorkers.extend(dismiss_list)289

290 defjoinAllDismissedWorkers(self):291 """Perform Thread.join() on all worker threads that have been dismissed.292 """

293 for worker inself.dismissedWorkers:294 worker.join()295 self.dismissedWorkers =[]296

297 def putRequest(self, request, block=True, timeout=None):298 """Put work request into work queue and save its id for later."""

299 assertisinstance(request, WorkRequest)300 #don't reuse old work requests

301 assert not getattr(request, 'exception', None)302 importtime303 self._requests_queue.put(request, block, timeout)304 self.workRequests[request.requestID] =request305

306 def poll(self, block=False):307 """Process any new results in the queue."""

308 whileTrue:309 #still results pending?

310 if notself.workRequests:311 raiseNoResultsPending312 #are there still workers to process remaining requests?

313 elif block and notself.workers:314 raiseNoWorkersAvailable315 try:316 #get back next results

317

318 request, result = self._results_queue.get(block=block)319

320 #has an exception occured?

321 if request.exception andrequest.exc_callback:322

323 request.exc_callback(request, result)324

325 #hand results to callback, if any

326 if request.callback and not\327 (request.exception andrequest.exc_callback):328 request.callback(request, result)329 delself.workRequests[request.requestID]330 exceptQueue.Empty:331 break

332

333 defwait(self):334 """Wait for results, blocking until all have arrived."""

335 while 1:336 try:337 self.poll(True)338 exceptNoResultsPending:339 break

340

341

342 ################

343 #USAGE EXAMPLE

344 ################

345

346 if __name__ == '__main__':347 importrandom348 importtime349

350 #the work the threads will have to do (rather trivial in our example)

351 defdo_something(data):352 time.sleep(random.randint(1,5))353 result = round(random.random() * data, 5)354 #just to show off, we throw an exception once in a while

355 if result > 5:356 raise RuntimeError("Something extraordinary happened!")357 returnresult358

359 #this will be called each time a result is available

360 defprint_result(request, result):361 print("**** Result from request #%s: %r" %(request.requestID, result))362

363 #this will be called when an exception occurs within a thread

364 #this example exception handler does little more than the default handler

365 defhandle_exception(request, exc_info):366 if notisinstance(exc_info, tuple):367 #Something is seriously wrong...

368 print(request)369 print(exc_info)370 raiseSystemExit371 print("**** Exception occured in request #%s: %s" %\372 (request.requestID, exc_info))373

374 #assemble the arguments for each job to a list...

375 data = [random.randint(1,10) for i in range(20)]376 #... and build a WorkRequest object for each item in data

377 requests =makeRequests(do_something, data, print_result, handle_exception)378 #to use the default exception handler, uncomment next line and comment out

379 #the preceding one.

380 #requests = makeRequests(do_something, data, print_result)

381

382 #or the other form of args_lists accepted by makeRequests: ((,), {})

383 data = [((random.randint(1,10),), {}) for i in range(20)]384 requests.extend(385 makeRequests(do_something, data, print_result, handle_exception)386 #makeRequests(do_something, data, print_result)

387 #to use the default exception handler, uncomment next line and comment

388 #out the preceding one.

389 )390

391 #we create a pool of 3 worker threads

392 print("Creating thread pool with 3 worker threads.")393 main = ThreadPool(3)394

395 #then we put the work requests in the queue...

396 for req inrequests:397 main.putRequest(req)398 print("Work request #%s added." %req.requestID)399 #or shorter:

400 #[main.putRequest(req) for req in requests]

401

402 #...and wait for the results to arrive in the result queue

403 #by using ThreadPool.wait(). This would block until results for

404 #all work requests have arrived:

405 #main.wait()

406

407 #instead we can poll for results while doing something else:

408 i =0409 whileTrue:410 try:411 time.sleep(0.5)412 main.poll()413 print("Main thread working...")414 print("(active worker threads: %i)" % (threading.activeCount()-1, ))415 if i == 10:416

417 main.createWorkers(3)418 if i == 20:419

420 main.dismissWorkers(2)421 i += 1

422 exceptKeyboardInterrupt:423 print("**** Interrupted!")424 break

425 exceptNoResultsPending:426 print("**** No pending results.")427 break

428 ifmain.dismissedWorkers:429 print("Joining all dismissed worker threads...")430 main.joinAllDismissedWorkers()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值