1 | // Copyright (C) 2016 The Qt Company Ltd. |
---|---|
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qthreadpool.h" |
5 | #include "qthreadpool_p.h" |
6 | #include "qdeadlinetimer.h" |
7 | #include "qcoreapplication.h" |
8 | |
9 | #include <QtCore/qpointer.h> |
10 | |
11 | #include <algorithm> |
12 | #include <memory> |
13 | |
14 | using namespace std::chrono_literals; |
15 | |
16 | QT_BEGIN_NAMESPACE |
17 | |
18 | using namespace Qt::StringLiterals; |
19 | |
20 | /* |
21 | QThread wrapper, provides synchronization against a ThreadPool |
22 | */ |
23 | class QThreadPoolThread : public QThread |
24 | { |
25 | Q_OBJECT |
26 | public: |
27 | QThreadPoolThread(QThreadPoolPrivate *manager); |
28 | void run() override; |
29 | void registerThreadInactive(); |
30 | |
31 | QWaitCondition runnableReady; |
32 | QThreadPoolPrivate *manager; |
33 | QRunnable *runnable; |
34 | }; |
35 | |
36 | /* |
37 | QThreadPool private class. |
38 | */ |
39 | |
40 | |
41 | /*! |
42 | \internal |
43 | */ |
44 | QThreadPoolThread::QThreadPoolThread(QThreadPoolPrivate *manager) |
45 | :manager(manager), runnable(nullptr) |
46 | { |
47 | setStackSize(manager->stackSize); |
48 | } |
49 | |
50 | /* |
51 | \internal |
52 | */ |
53 | void QThreadPoolThread::run() |
54 | { |
55 | QMutexLocker locker(&manager->mutex); |
56 | for(;;) { |
57 | QRunnable *r = runnable; |
58 | runnable = nullptr; |
59 | |
60 | do { |
61 | if (r) { |
62 | // If autoDelete() is false, r might already be deleted after run(), so check status now. |
63 | const bool del = r->autoDelete(); |
64 | |
65 | // run the task |
66 | locker.unlock(); |
67 | #ifndef QT_NO_EXCEPTIONS |
68 | try { |
69 | #endif |
70 | r->run(); |
71 | #ifndef QT_NO_EXCEPTIONS |
72 | } catch (...) { |
73 | qWarning(msg: "Qt Concurrent has caught an exception thrown from a worker thread.\n" |
74 | "This is not supported, exceptions thrown in worker threads must be\n" |
75 | "caught before control returns to Qt Concurrent."); |
76 | registerThreadInactive(); |
77 | throw; |
78 | } |
79 | #endif |
80 | |
81 | if (del) |
82 | delete r; |
83 | locker.relock(); |
84 | } |
85 | |
86 | // if too many threads are active, stop working in this one |
87 | if (manager->tooManyThreadsActive()) |
88 | break; |
89 | |
90 | // all work is done, time to wait for more |
91 | if (manager->queue.isEmpty()) |
92 | break; |
93 | |
94 | QueuePage *page = manager->queue.constFirst(); |
95 | r = page->pop(); |
96 | |
97 | if (page->isFinished()) { |
98 | manager->queue.removeFirst(); |
99 | delete page; |
100 | } |
101 | } while (true); |
102 | |
103 | // this thread is about to be deleted, do not wait or expire |
104 | if (!manager->allThreads.contains(value: this)) { |
105 | registerThreadInactive(); |
106 | return; |
107 | } |
108 | |
109 | // if too many threads are active, expire this thread |
110 | if (manager->tooManyThreadsActive()) { |
111 | manager->expiredThreads.enqueue(t: this); |
112 | registerThreadInactive(); |
113 | return; |
114 | } |
115 | manager->waitingThreads.enqueue(t: this); |
116 | registerThreadInactive(); |
117 | // wait for work, exiting after the expiry timeout is reached |
118 | runnableReady.wait(lockedMutex: locker.mutex(), deadline: QDeadlineTimer(manager->expiryTimeout)); |
119 | // this thread is about to be deleted, do not work or expire |
120 | if (!manager->allThreads.contains(value: this)) { |
121 | Q_ASSERT(manager->queue.isEmpty()); |
122 | return; |
123 | } |
124 | if (manager->waitingThreads.removeOne(t: this)) { |
125 | manager->expiredThreads.enqueue(t: this); |
126 | return; |
127 | } |
128 | ++manager->activeThreads; |
129 | } |
130 | } |
131 | |
132 | void QThreadPoolThread::registerThreadInactive() |
133 | { |
134 | if (--manager->activeThreads == 0) |
135 | manager->noActiveThreads.wakeAll(); |
136 | } |
137 | |
138 | |
139 | /* |
140 | \internal |
141 | */ |
142 | QThreadPoolPrivate:: QThreadPoolPrivate() |
143 | { } |
144 | |
145 | bool QThreadPoolPrivate::tryStart(QRunnable *task) |
146 | { |
147 | Q_ASSERT(task != nullptr); |
148 | if (allThreads.isEmpty()) { |
149 | // always create at least one thread |
150 | startThread(runnable: task); |
151 | return true; |
152 | } |
153 | |
154 | // can't do anything if we're over the limit |
155 | if (areAllThreadsActive()) |
156 | return false; |
157 | |
158 | if (!waitingThreads.isEmpty()) { |
159 | // recycle an available thread |
160 | enqueueTask(task); |
161 | waitingThreads.takeFirst()->runnableReady.wakeOne(); |
162 | return true; |
163 | } |
164 | |
165 | if (!expiredThreads.isEmpty()) { |
166 | // restart an expired thread |
167 | QThreadPoolThread *thread = expiredThreads.dequeue(); |
168 | Q_ASSERT(thread->runnable == nullptr); |
169 | |
170 | ++activeThreads; |
171 | |
172 | thread->runnable = task; |
173 | |
174 | // Ensure that the thread has actually finished, otherwise the following |
175 | // start() has no effect. |
176 | thread->wait(); |
177 | Q_ASSERT(thread->isFinished()); |
178 | thread->start(threadPriority); |
179 | return true; |
180 | } |
181 | |
182 | // start a new thread |
183 | startThread(runnable: task); |
184 | return true; |
185 | } |
186 | |
187 | inline bool comparePriority(int priority, const QueuePage *p) |
188 | { |
189 | return p->priority() < priority; |
190 | } |
191 | |
192 | void QThreadPoolPrivate::enqueueTask(QRunnable *runnable, int priority) |
193 | { |
194 | Q_ASSERT(runnable != nullptr); |
195 | for (QueuePage *page : std::as_const(t&: queue)) { |
196 | if (page->priority() == priority && !page->isFull()) { |
197 | page->push(runnable); |
198 | return; |
199 | } |
200 | } |
201 | auto it = std::upper_bound(first: queue.constBegin(), last: queue.constEnd(), val: priority, comp: comparePriority); |
202 | queue.insert(i: std::distance(first: queue.constBegin(), last: it), t: new QueuePage(runnable, priority)); |
203 | } |
204 | |
205 | int QThreadPoolPrivate::activeThreadCount() const |
206 | { |
207 | return (allThreads.size() |
208 | - expiredThreads.size() |
209 | - waitingThreads.size() |
210 | + reservedThreads); |
211 | } |
212 | |
213 | void QThreadPoolPrivate::tryToStartMoreThreads() |
214 | { |
215 | // try to push tasks on the queue to any available threads |
216 | while (!queue.isEmpty()) { |
217 | QueuePage *page = queue.constFirst(); |
218 | if (!tryStart(task: page->first())) |
219 | break; |
220 | |
221 | page->pop(); |
222 | |
223 | if (page->isFinished()) { |
224 | queue.removeFirst(); |
225 | delete page; |
226 | } |
227 | } |
228 | } |
229 | |
230 | bool QThreadPoolPrivate::areAllThreadsActive() const |
231 | { |
232 | const int activeThreadCount = this->activeThreadCount(); |
233 | return activeThreadCount >= maxThreadCount() && (activeThreadCount - reservedThreads) >= 1; |
234 | } |
235 | |
236 | bool QThreadPoolPrivate::tooManyThreadsActive() const |
237 | { |
238 | const int activeThreadCount = this->activeThreadCount(); |
239 | return activeThreadCount > maxThreadCount() && (activeThreadCount - reservedThreads) > 1; |
240 | } |
241 | |
242 | /*! |
243 | \internal |
244 | */ |
245 | void QThreadPoolPrivate::startThread(QRunnable *runnable) |
246 | { |
247 | Q_ASSERT(runnable != nullptr); |
248 | auto thread = std::make_unique<QThreadPoolThread>(args: this); |
249 | if (objectName.isEmpty()) |
250 | objectName = u"Thread (pooled)"_s; |
251 | thread->setObjectName(objectName); |
252 | Q_ASSERT(!allThreads.contains(thread.get())); // if this assert hits, we have an ABA problem (deleted threads don't get removed here) |
253 | allThreads.insert(value: thread.get()); |
254 | ++activeThreads; |
255 | |
256 | thread->runnable = runnable; |
257 | thread.release()->start(threadPriority); |
258 | } |
259 | |
260 | /*! |
261 | \internal |
262 | |
263 | Helper function only to be called from waitForDone() |
264 | |
265 | Deletes all current threads. |
266 | */ |
267 | void QThreadPoolPrivate::reset() |
268 | { |
269 | // move the contents of the set out so that we can iterate without the lock |
270 | auto allThreadsCopy = std::exchange(obj&: allThreads, new_val: {}); |
271 | expiredThreads.clear(); |
272 | waitingThreads.clear(); |
273 | |
274 | mutex.unlock(); |
275 | |
276 | for (QThreadPoolThread *thread : std::as_const(t&: allThreadsCopy)) { |
277 | if (thread->isRunning()) { |
278 | thread->runnableReady.wakeAll(); |
279 | thread->wait(); |
280 | } |
281 | delete thread; |
282 | } |
283 | |
284 | mutex.lock(); |
285 | } |
286 | |
287 | /*! |
288 | \internal |
289 | |
290 | Helper function only to be called from the public waitForDone() |
291 | */ |
292 | bool QThreadPoolPrivate::waitForDone(const QDeadlineTimer &timer) |
293 | { |
294 | QMutexLocker locker(&mutex); |
295 | while (!(queue.isEmpty() && activeThreads == 0) && !timer.hasExpired()) |
296 | noActiveThreads.wait(lockedMutex: &mutex, deadline: timer); |
297 | |
298 | if (!queue.isEmpty() || activeThreads) |
299 | return false; |
300 | |
301 | reset(); |
302 | // New jobs might have started during reset, but return anyway |
303 | // as the active thread and task count did reach 0 once, and |
304 | // race conditions are outside our scope. |
305 | return true; |
306 | } |
307 | |
308 | void QThreadPoolPrivate::clear() |
309 | { |
310 | QMutexLocker locker(&mutex); |
311 | while (!queue.isEmpty()) { |
312 | auto *page = queue.takeLast(); |
313 | while (!page->isFinished()) { |
314 | QRunnable *r = page->pop(); |
315 | if (r && r->autoDelete()) { |
316 | locker.unlock(); |
317 | delete r; |
318 | locker.relock(); |
319 | } |
320 | } |
321 | delete page; |
322 | } |
323 | } |
324 | |
325 | /*! |
326 | \since 5.9 |
327 | |
328 | Attempts to remove the specified \a runnable from the queue if it is not yet started. |
329 | If the runnable had not been started, returns \c true, and ownership of \a runnable |
330 | is transferred to the caller (even when \c{runnable->autoDelete() == true}). |
331 | Otherwise returns \c false. |
332 | |
333 | \note If \c{runnable->autoDelete() == true}, this function may remove the wrong |
334 | runnable. This is known as the \l{https://en.wikipedia.org/wiki/ABA_problem}{ABA problem}: |
335 | the original \a runnable may already have executed and has since been deleted. |
336 | The memory is re-used for another runnable, which then gets removed instead of |
337 | the intended one. For this reason, we recommend calling this function only for |
338 | runnables that are not auto-deleting. |
339 | |
340 | \sa start(), QRunnable::autoDelete() |
341 | */ |
342 | bool QThreadPool::tryTake(QRunnable *runnable) |
343 | { |
344 | Q_D(QThreadPool); |
345 | |
346 | if (runnable == nullptr) |
347 | return false; |
348 | |
349 | QMutexLocker locker(&d->mutex); |
350 | for (QueuePage *page : std::as_const(t&: d->queue)) { |
351 | if (page->tryTake(runnable)) { |
352 | if (page->isFinished()) { |
353 | d->queue.removeOne(t: page); |
354 | delete page; |
355 | } |
356 | return true; |
357 | } |
358 | } |
359 | |
360 | return false; |
361 | } |
362 | |
363 | /*! |
364 | \internal |
365 | Searches for \a runnable in the queue, removes it from the queue and |
366 | runs it if found. This function does not return until the runnable |
367 | has completed. |
368 | */ |
369 | void QThreadPoolPrivate::stealAndRunRunnable(QRunnable *runnable) |
370 | { |
371 | Q_Q(QThreadPool); |
372 | if (!q->tryTake(runnable)) |
373 | return; |
374 | // If autoDelete() is false, runnable might already be deleted after run(), so check status now. |
375 | const bool del = runnable->autoDelete(); |
376 | |
377 | runnable->run(); |
378 | |
379 | if (del) |
380 | delete runnable; |
381 | } |
382 | |
383 | /*! |
384 | \class QThreadPool |
385 | \inmodule QtCore |
386 | \brief The QThreadPool class manages a collection of QThreads. |
387 | \since 4.4 |
388 | \threadsafe |
389 | |
390 | \ingroup thread |
391 | |
392 | QThreadPool manages and recycles individual QThread objects to help reduce |
393 | thread creation costs in programs that use threads. Each Qt application |
394 | has one global QThreadPool object, which can be accessed by calling |
395 | globalInstance(). |
396 | |
397 | To use one of the QThreadPool threads, subclass QRunnable and implement |
398 | the run() virtual function. Then create an object of that class and pass |
399 | it to QThreadPool::start(). |
400 | |
401 | \snippet code/src_corelib_concurrent_qthreadpool.cpp 0 |
402 | |
403 | QThreadPool deletes the QRunnable automatically by default. Use |
404 | QRunnable::setAutoDelete() to change the auto-deletion flag. |
405 | |
406 | QThreadPool supports executing the same QRunnable more than once |
407 | by calling tryStart(this) from within QRunnable::run(). |
408 | If autoDelete is enabled the QRunnable will be deleted when |
409 | the last thread exits the run function. Calling start() |
410 | multiple times with the same QRunnable when autoDelete is enabled |
411 | creates a race condition and is not recommended. |
412 | |
413 | Threads that are unused for a certain amount of time will expire. The |
414 | default expiry timeout is 30000 milliseconds (30 seconds). This can be |
415 | changed using setExpiryTimeout(). Setting a negative expiry timeout |
416 | disables the expiry mechanism. |
417 | |
418 | Call maxThreadCount() to query the maximum number of threads to be used. |
419 | If needed, you can change the limit with setMaxThreadCount(). The default |
420 | maxThreadCount() is QThread::idealThreadCount(). The activeThreadCount() |
421 | function returns the number of threads currently doing work. |
422 | |
423 | The reserveThread() function reserves a thread for external |
424 | use. Use releaseThread() when your are done with the thread, so |
425 | that it may be reused. Essentially, these functions temporarily |
426 | increase or reduce the active thread count and are useful when |
427 | implementing time-consuming operations that are not visible to the |
428 | QThreadPool. |
429 | |
430 | Note that QThreadPool is a low-level class for managing threads, see |
431 | the Qt Concurrent module for higher level alternatives. |
432 | |
433 | \sa QRunnable |
434 | */ |
435 | |
436 | /*! |
437 | Constructs a thread pool with the given \a parent. |
438 | */ |
439 | QThreadPool::QThreadPool(QObject *parent) |
440 | : QObject(*new QThreadPoolPrivate, parent) |
441 | { |
442 | Q_D(QThreadPool); |
443 | connect(sender: this, signal: &QObject::objectNameChanged, context: this, slot: [d](const QString &newName) { |
444 | // We keep a copy of the name under our own lock, so we can access it thread-safely. |
445 | QMutexLocker locker(&d->mutex); |
446 | d->objectName = newName; |
447 | }); |
448 | } |
449 | |
450 | /*! |
451 | Destroys the QThreadPool. |
452 | This function will block until all runnables have been completed. |
453 | */ |
454 | QThreadPool::~QThreadPool() |
455 | { |
456 | Q_D(QThreadPool); |
457 | waitForDone(); |
458 | Q_ASSERT(d->queue.isEmpty()); |
459 | Q_ASSERT(d->allThreads.isEmpty()); |
460 | } |
461 | |
462 | /*! |
463 | Returns the global QThreadPool instance. |
464 | */ |
465 | QThreadPool *QThreadPool::globalInstance() |
466 | { |
467 | Q_CONSTINIT static QPointer<QThreadPool> theInstance; |
468 | Q_CONSTINIT static QBasicMutex theMutex; |
469 | |
470 | const QMutexLocker locker(&theMutex); |
471 | if (theInstance.isNull() && !QCoreApplication::closingDown()) |
472 | theInstance = new QThreadPool(); |
473 | return theInstance; |
474 | } |
475 | |
476 | /*! |
477 | Returns the QThreadPool instance for Qt Gui. |
478 | \internal |
479 | */ |
480 | QThreadPool *QThreadPoolPrivate::qtGuiInstance() |
481 | { |
482 | Q_CONSTINIT static QPointer<QThreadPool> guiInstance; |
483 | Q_CONSTINIT static QBasicMutex theMutex; |
484 | |
485 | const QMutexLocker locker(&theMutex); |
486 | if (guiInstance.isNull() && !QCoreApplication::closingDown()) { |
487 | guiInstance = new QThreadPool(); |
488 | // Limit max thread to avoid too many parallel threads. |
489 | // We are not optimized for much more than 4 or 8 threads. |
490 | if (guiInstance && guiInstance->maxThreadCount() > 4) |
491 | guiInstance->setMaxThreadCount(qBound(min: 4, val: guiInstance->maxThreadCount() / 2, max: 8)); |
492 | } |
493 | return guiInstance; |
494 | } |
495 | |
496 | /*! |
497 | Reserves a thread and uses it to run \a runnable, unless this thread will |
498 | make the current thread count exceed maxThreadCount(). In that case, |
499 | \a runnable is added to a run queue instead. The \a priority argument can |
500 | be used to control the run queue's order of execution. |
501 | |
502 | Note that the thread pool takes ownership of the \a runnable if |
503 | \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c true, |
504 | and the \a runnable will be deleted automatically by the thread |
505 | pool after the \l{QRunnable::run()}{runnable->run()} returns. If |
506 | \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c false, |
507 | ownership of \a runnable remains with the caller. Note that |
508 | changing the auto-deletion on \a runnable after calling this |
509 | functions results in undefined behavior. |
510 | */ |
511 | void QThreadPool::start(QRunnable *runnable, int priority) |
512 | { |
513 | if (!runnable) |
514 | return; |
515 | |
516 | Q_D(QThreadPool); |
517 | QMutexLocker locker(&d->mutex); |
518 | |
519 | if (!d->tryStart(task: runnable)) |
520 | d->enqueueTask(runnable, priority); |
521 | } |
522 | |
523 | /*! |
524 | \fn template<typename Callable, QRunnable::if_callable<Callable>> void QThreadPool::start(Callable &&callableToRun, int priority) |
525 | \overload |
526 | \since 5.15 |
527 | |
528 | Reserves a thread and uses it to run \a callableToRun, unless this thread will |
529 | make the current thread count exceed maxThreadCount(). In that case, |
530 | \a callableToRun is added to a run queue instead. The \a priority argument can |
531 | be used to control the run queue's order of execution. |
532 | |
533 | \note This function participates in overload resolution only if \c Callable |
534 | is a function or function object which can be called with zero arguments. |
535 | |
536 | \note In Qt version prior to 6.6, this function took std::function<void()>, |
537 | and therefore couldn't handle move-only callables. |
538 | */ |
539 | |
540 | /*! |
541 | Attempts to reserve a thread to run \a runnable. |
542 | |
543 | If no threads are available at the time of calling, then this function |
544 | does nothing and returns \c false. Otherwise, \a runnable is run immediately |
545 | using one available thread and this function returns \c true. |
546 | |
547 | Note that on success the thread pool takes ownership of the \a runnable if |
548 | \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c true, |
549 | and the \a runnable will be deleted automatically by the thread |
550 | pool after the \l{QRunnable::run()}{runnable->run()} returns. If |
551 | \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c false, |
552 | ownership of \a runnable remains with the caller. Note that |
553 | changing the auto-deletion on \a runnable after calling this |
554 | function results in undefined behavior. |
555 | */ |
556 | bool QThreadPool::tryStart(QRunnable *runnable) |
557 | { |
558 | if (!runnable) |
559 | return false; |
560 | |
561 | Q_D(QThreadPool); |
562 | QMutexLocker locker(&d->mutex); |
563 | if (d->tryStart(task: runnable)) |
564 | return true; |
565 | |
566 | return false; |
567 | } |
568 | |
569 | /*! |
570 | \fn template<typename Callable, QRunnable::if_callable<Callable>> bool QThreadPool::tryStart(Callable &&callableToRun) |
571 | \overload |
572 | \since 5.15 |
573 | Attempts to reserve a thread to run \a callableToRun. |
574 | |
575 | If no threads are available at the time of calling, then this function |
576 | does nothing and returns \c false. Otherwise, \a callableToRun is run immediately |
577 | using one available thread and this function returns \c true. |
578 | |
579 | \note This function participates in overload resolution only if \c Callable |
580 | is a function or function object which can be called with zero arguments. |
581 | |
582 | \note In Qt version prior to 6.6, this function took std::function<void()>, |
583 | and therefore couldn't handle move-only callables. |
584 | */ |
585 | |
586 | /*! \property QThreadPool::expiryTimeout |
587 | \brief the thread expiry timeout value in milliseconds. |
588 | |
589 | Threads that are unused for \e expiryTimeout milliseconds are considered |
590 | to have expired and will exit. Such threads will be restarted as needed. |
591 | The default \a expiryTimeout is 30000 milliseconds (30 seconds). If |
592 | \a expiryTimeout is negative, newly created threads will not expire, e.g., |
593 | they will not exit until the thread pool is destroyed. |
594 | |
595 | Note that setting \a expiryTimeout has no effect on already running |
596 | threads. Only newly created threads will use the new \a expiryTimeout. |
597 | We recommend setting the \a expiryTimeout immediately after creating the |
598 | thread pool, but before calling start(). |
599 | */ |
600 | |
601 | int QThreadPool::expiryTimeout() const |
602 | { |
603 | using namespace std::chrono; |
604 | Q_D(const QThreadPool); |
605 | QMutexLocker locker(&d->mutex); |
606 | if (d->expiryTimeout == decltype(d->expiryTimeout)::max()) |
607 | return -1; |
608 | return duration_cast<milliseconds>(d: d->expiryTimeout).count(); |
609 | } |
610 | |
611 | void QThreadPool::setExpiryTimeout(int expiryTimeout) |
612 | { |
613 | Q_D(QThreadPool); |
614 | QMutexLocker locker(&d->mutex); |
615 | if (expiryTimeout < 0) |
616 | d->expiryTimeout = decltype(d->expiryTimeout)::max(); |
617 | else |
618 | d->expiryTimeout = expiryTimeout * 1ms; |
619 | } |
620 | |
621 | /*! \property QThreadPool::maxThreadCount |
622 | |
623 | \brief the maximum number of threads used by the thread pool. This property |
624 | will default to the value of QThread::idealThreadCount() at the moment the |
625 | QThreadPool object is created. |
626 | |
627 | \note The thread pool will always use at least 1 thread, even if |
628 | \a maxThreadCount limit is zero or negative. |
629 | |
630 | The default \a maxThreadCount is QThread::idealThreadCount(). |
631 | */ |
632 | |
633 | int QThreadPool::maxThreadCount() const |
634 | { |
635 | Q_D(const QThreadPool); |
636 | QMutexLocker locker(&d->mutex); |
637 | return d->requestedMaxThreadCount; |
638 | } |
639 | |
640 | void QThreadPool::setMaxThreadCount(int maxThreadCount) |
641 | { |
642 | Q_D(QThreadPool); |
643 | QMutexLocker locker(&d->mutex); |
644 | |
645 | if (maxThreadCount == d->requestedMaxThreadCount) |
646 | return; |
647 | |
648 | d->requestedMaxThreadCount = maxThreadCount; |
649 | d->tryToStartMoreThreads(); |
650 | } |
651 | |
652 | /*! \property QThreadPool::activeThreadCount |
653 | |
654 | \brief the number of active threads in the thread pool. |
655 | |
656 | \note It is possible for this function to return a value that is greater |
657 | than maxThreadCount(). See reserveThread() for more details. |
658 | |
659 | \sa reserveThread(), releaseThread() |
660 | */ |
661 | |
662 | int QThreadPool::activeThreadCount() const |
663 | { |
664 | Q_D(const QThreadPool); |
665 | QMutexLocker locker(&d->mutex); |
666 | return d->activeThreadCount(); |
667 | } |
668 | |
669 | /*! |
670 | Reserves one thread, disregarding activeThreadCount() and maxThreadCount(). |
671 | |
672 | Once you are done with the thread, call releaseThread() to allow it to be |
673 | reused. |
674 | |
675 | \note Even if reserving maxThreadCount() threads or more, the thread pool |
676 | will still allow a minimum of one thread. |
677 | |
678 | \note This function will increase the reported number of active threads. |
679 | This means that by using this function, it is possible for |
680 | activeThreadCount() to return a value greater than maxThreadCount() . |
681 | |
682 | \sa releaseThread() |
683 | */ |
684 | void QThreadPool::reserveThread() |
685 | { |
686 | Q_D(QThreadPool); |
687 | QMutexLocker locker(&d->mutex); |
688 | ++d->reservedThreads; |
689 | } |
690 | |
691 | /*! \property QThreadPool::stackSize |
692 | \brief the stack size for the thread pool worker threads. |
693 | |
694 | The value of the property is only used when the thread pool creates |
695 | new threads. Changing it has no effect for already created |
696 | or running threads. |
697 | |
698 | The default value is 0, which makes QThread use the operating |
699 | system default stack size. |
700 | |
701 | \since 5.10 |
702 | */ |
703 | void QThreadPool::setStackSize(uint stackSize) |
704 | { |
705 | Q_D(QThreadPool); |
706 | QMutexLocker locker(&d->mutex); |
707 | d->stackSize = stackSize; |
708 | } |
709 | |
710 | uint QThreadPool::stackSize() const |
711 | { |
712 | Q_D(const QThreadPool); |
713 | QMutexLocker locker(&d->mutex); |
714 | return d->stackSize; |
715 | } |
716 | |
717 | /*! \property QThreadPool::threadPriority |
718 | \brief the thread priority for new worker threads. |
719 | |
720 | The value of the property is only used when the thread pool starts |
721 | new threads. Changing it has no effect for already running threads. |
722 | |
723 | The default value is QThread::InheritPriority, which makes QThread |
724 | use the same priority as the one the QThreadPool object lives in. |
725 | |
726 | \sa QThread::Priority |
727 | |
728 | \since 6.2 |
729 | */ |
730 | |
731 | void QThreadPool::setThreadPriority(QThread::Priority priority) |
732 | { |
733 | Q_D(QThreadPool); |
734 | QMutexLocker locker(&d->mutex); |
735 | d->threadPriority = priority; |
736 | } |
737 | |
738 | QThread::Priority QThreadPool::threadPriority() const |
739 | { |
740 | Q_D(const QThreadPool); |
741 | QMutexLocker locker(&d->mutex); |
742 | return d->threadPriority; |
743 | } |
744 | |
745 | /*! |
746 | Releases a thread previously reserved by a call to reserveThread(). |
747 | |
748 | \note Calling this function without previously reserving a thread |
749 | temporarily increases maxThreadCount(). This is useful when a |
750 | thread goes to sleep waiting for more work, allowing other threads |
751 | to continue. Be sure to call reserveThread() when done waiting, so |
752 | that the thread pool can correctly maintain the |
753 | activeThreadCount(). |
754 | |
755 | \sa reserveThread() |
756 | */ |
757 | void QThreadPool::releaseThread() |
758 | { |
759 | Q_D(QThreadPool); |
760 | QMutexLocker locker(&d->mutex); |
761 | --d->reservedThreads; |
762 | d->tryToStartMoreThreads(); |
763 | } |
764 | |
765 | /*! |
766 | Releases a thread previously reserved with reserveThread() and uses it |
767 | to run \a runnable. |
768 | |
769 | Note that the thread pool takes ownership of the \a runnable if |
770 | \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c true, |
771 | and the \a runnable will be deleted automatically by the thread |
772 | pool after the \l{QRunnable::run()}{runnable->run()} returns. If |
773 | \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c false, |
774 | ownership of \a runnable remains with the caller. Note that |
775 | changing the auto-deletion on \a runnable after calling this |
776 | functions results in undefined behavior. |
777 | |
778 | \note Calling this when no threads are reserved results in |
779 | undefined behavior. |
780 | |
781 | \since 6.3 |
782 | \sa reserveThread(), start() |
783 | */ |
784 | void QThreadPool::startOnReservedThread(QRunnable *runnable) |
785 | { |
786 | if (!runnable) |
787 | return releaseThread(); |
788 | |
789 | Q_D(QThreadPool); |
790 | QMutexLocker locker(&d->mutex); |
791 | Q_ASSERT(d->reservedThreads > 0); |
792 | --d->reservedThreads; |
793 | |
794 | if (!d->tryStart(task: runnable)) { |
795 | // This can only happen if we reserved max threads, |
796 | // and something took the one minimum thread. |
797 | d->enqueueTask(runnable, INT_MAX); |
798 | } |
799 | } |
800 | |
801 | /*! |
802 | \fn template<typename Callable, QRunnable::if_callable<Callable>> void QThreadPool::startOnReservedThread(Callable &&callableToRun) |
803 | \overload |
804 | \since 6.3 |
805 | |
806 | Releases a thread previously reserved with reserveThread() and uses it |
807 | to run \a callableToRun. |
808 | |
809 | \note This function participates in overload resolution only if \c Callable |
810 | is a function or function object which can be called with zero arguments. |
811 | |
812 | \note In Qt version prior to 6.6, this function took std::function<void()>, |
813 | and therefore couldn't handle move-only callables. |
814 | */ |
815 | |
816 | /*! |
817 | \fn bool QThreadPool::waitForDone(int msecs) |
818 | Waits up to \a msecs milliseconds for all threads to exit and removes all |
819 | threads from the thread pool. Returns \c true if all threads were removed; |
820 | otherwise it returns \c false. If \a msecs is -1, this function waits for |
821 | the last thread to exit. |
822 | */ |
823 | |
824 | /*! |
825 | \since 6.8 |
826 | |
827 | Waits until \a deadline expires for all threads to exit and removes all |
828 | threads from the thread pool. Returns \c true if all threads were removed; |
829 | otherwise it returns \c false. |
830 | */ |
831 | bool QThreadPool::waitForDone(QDeadlineTimer deadline) |
832 | { |
833 | Q_D(QThreadPool); |
834 | return d->waitForDone(timer: deadline); |
835 | } |
836 | |
837 | /*! |
838 | \since 5.2 |
839 | |
840 | Removes the runnables that are not yet started from the queue. |
841 | The runnables for which \l{QRunnable::autoDelete()}{runnable->autoDelete()} |
842 | returns \c true are deleted. |
843 | |
844 | \sa start() |
845 | */ |
846 | void QThreadPool::clear() |
847 | { |
848 | Q_D(QThreadPool); |
849 | d->clear(); |
850 | } |
851 | |
852 | /*! |
853 | \since 6.0 |
854 | |
855 | Returns \c true if \a thread is a thread managed by this thread pool. |
856 | */ |
857 | bool QThreadPool::contains(const QThread *thread) const |
858 | { |
859 | Q_D(const QThreadPool); |
860 | const QThreadPoolThread *poolThread = qobject_cast<const QThreadPoolThread *>(object: thread); |
861 | if (!poolThread) |
862 | return false; |
863 | QMutexLocker locker(&d->mutex); |
864 | return d->allThreads.contains(value: const_cast<QThreadPoolThread *>(poolThread)); |
865 | } |
866 | |
867 | QT_END_NAMESPACE |
868 | |
869 | #include "moc_qthreadpool.cpp" |
870 | #include "qthreadpool.moc" |
871 |
Definitions
- QThreadPoolThread
- QThreadPoolThread
- run
- registerThreadInactive
- QThreadPoolPrivate
- tryStart
- comparePriority
- enqueueTask
- activeThreadCount
- tryToStartMoreThreads
- areAllThreadsActive
- tooManyThreadsActive
- startThread
- reset
- waitForDone
- clear
- tryTake
- stealAndRunRunnable
- QThreadPool
- ~QThreadPool
- globalInstance
- qtGuiInstance
- start
- tryStart
- expiryTimeout
- setExpiryTimeout
- maxThreadCount
- setMaxThreadCount
- activeThreadCount
- reserveThread
- setStackSize
- stackSize
- setThreadPriority
- threadPriority
- releaseThread
- startOnReservedThread
- waitForDone
- clear
Learn to use CMake with our Intro Training
Find out more