mirror of
https://github.com/Pithikos/C-Thread-Pool
synced 2024-11-21 21:21:23 +03:00
Merge branch 'master' into gonzus/fix-function-pointer-casts
This commit is contained in:
commit
f8e457b3b4
4
.circleci/Dockerfile
Normal file
4
.circleci/Dockerfile
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
FROM ubuntu
|
||||||
|
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get -y install gcc valgrind time python
|
4
.circleci/README.md
Normal file
4
.circleci/README.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
Build and push container
|
||||||
|
|
||||||
|
docker build -t pithikos/test-c-thread-pool .
|
||||||
|
docker push pithikos/test-c-thread-pool
|
19
.circleci/config.yml
Normal file
19
.circleci/config.yml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
version: 2.1
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
docker:
|
||||||
|
- image: pithikos/test-c-thread-pool
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: Test
|
||||||
|
command: |
|
||||||
|
cd tests/
|
||||||
|
./normal_compile.sh
|
||||||
|
./optimized_compile.sh
|
||||||
|
|
||||||
|
workflows:
|
||||||
|
test:
|
||||||
|
jobs:
|
||||||
|
- test
|
@ -1,4 +1,4 @@
|
|||||||
![Build status](http://178.62.170.124:3000/pithikos/c-thread-pool/badge/?branch=master)
|
[![CircleCI](https://circleci.com/gh/Pithikos/C-Thread-Pool.svg?style=svg)](https://circleci.com/gh/Pithikos/C-Thread-Pool)
|
||||||
|
|
||||||
# C Thread Pool
|
# C Thread Pool
|
||||||
|
|
||||||
|
@ -1,47 +1,47 @@
|
|||||||
## High level
|
## High level
|
||||||
|
|
||||||
Description: Library providing a threading pool where you can add work on the fly. The number
|
Description: Library providing a threading pool where you can add work on the fly. The number
|
||||||
of threads in the pool is adjustable when creating the pool. In most cases
|
of threads in the pool is adjustable when creating the pool. In most cases
|
||||||
this should equal the number of threads supported by your cpu.
|
this should equal the number of threads supported by your cpu.
|
||||||
|
|
||||||
For an example on how to use the threadpool, check the main.c file or just read
|
For an example on how to use the threadpool, check the main.c file or just read
|
||||||
the documentation found in the README.md file.
|
the documentation found in the README.md file.
|
||||||
|
|
||||||
In this header file a detailed overview of the functions and the threadpool's logical
|
In this header file a detailed overview of the functions and the threadpool's logical
|
||||||
scheme is presented in case you wish to tweak or alter something.
|
scheme is presented in case you wish to tweak or alter something.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_______________________________________________________
|
_______________________________________________________
|
||||||
/ \
|
/ \
|
||||||
| JOB QUEUE | job1 | job2 | job3 | job4 | .. |
|
| JOB QUEUE | job1 | job2 | job3 | job4 | .. |
|
||||||
| |
|
| |
|
||||||
| threadpool | thread1 | thread2 | .. |
|
| threadpool | thread1 | thread2 | .. |
|
||||||
\_______________________________________________________/
|
\_______________________________________________________/
|
||||||
|
|
||||||
|
|
||||||
Description: Jobs are added to the job queue. Once a thread in the pool
|
Description: Jobs are added to the job queue. Once a thread in the pool
|
||||||
is idle, it is assigned with the first job from the queue(and
|
is idle, it is assigned the first job from the queue (and that job is
|
||||||
erased from the queue). It's each thread's job to read from
|
erased from the queue). It is each thread's job to read from
|
||||||
the queue serially(using lock) and executing each job
|
the queue serially (using lock) and executing each job
|
||||||
until the queue is empty.
|
until the queue is empty.
|
||||||
|
|
||||||
|
|
||||||
Scheme:
|
Scheme:
|
||||||
|
|
||||||
thpool______ jobqueue____ ______
|
thpool______ jobqueue____ ______
|
||||||
| | | | .----------->|_job0_| Newly added job
|
| | | | .----------->|_job0_| Newly added job
|
||||||
| | | rear ----------' |_job1_|
|
| | | rear ----------' |_job1_|
|
||||||
| jobqueue----------------->| | |_job2_|
|
| jobqueue----------------->| | |_job2_|
|
||||||
| | | front ----------. |__..__|
|
| | | front ----------. |__..__|
|
||||||
|___________| |___________| '----------->|_jobn_| Job for thread to take
|
|___________| |___________| '----------->|_jobn_| Job for thread to take
|
||||||
|
|
||||||
|
|
||||||
job0________
|
job0________
|
||||||
| |
|
| |
|
||||||
| function---->
|
| function---->
|
||||||
| |
|
| |
|
||||||
| arg------->
|
| arg------->
|
||||||
| | job1________
|
| | job1________
|
||||||
| next-------------->| |
|
| next-------------->| |
|
||||||
|___________| | |..
|
|___________| | |..
|
||||||
|
25
docs/FAQ.md
25
docs/FAQ.md
@ -1,7 +1,7 @@
|
|||||||
|
### Why isn't `pthread_exit()` used to exit a thread?
|
||||||
|
|
||||||
###Why isn't pthread_exit() used to exit a thread?
|
`thread_do` used to use `pthread_exit()`. However that resulted in
|
||||||
`thread_do` used to use pthread_exit(). However that resulted in
|
hard times of testing for memory leaks. The reason is that on `pthread_exit()`
|
||||||
hard times of testing for memory leaks. The reason is that on pthread_exit()
|
|
||||||
not all memory is freed bt pthread (probably for future threads or false
|
not all memory is freed bt pthread (probably for future threads or false
|
||||||
belief that the application is terminating). For these reasons a simple return
|
belief that the application is terminating). For these reasons a simple return
|
||||||
is used.
|
is used.
|
||||||
@ -9,27 +9,28 @@ is used.
|
|||||||
Interestingly using `pthread_exit()` results in much more memory being allocated.
|
Interestingly using `pthread_exit()` results in much more memory being allocated.
|
||||||
|
|
||||||
|
|
||||||
###Why do you use sleep() after calling thpool_destroy()?
|
### Why do you use `sleep()` after calling `thpool_destroy()`?
|
||||||
This is needed only in the tests. The reason is that if you call thpool_destroy
|
|
||||||
and then exit immedietely, maybe the program will exit before all the threads
|
This is needed only in the tests. The reason is that if you call `thpool_destroy()`
|
||||||
|
and then exit immediately, maybe the program will exit before all the threads
|
||||||
had the time to deallocate. In that way it is impossible to check for memory
|
had the time to deallocate. In that way it is impossible to check for memory
|
||||||
leaks.
|
leaks.
|
||||||
|
|
||||||
In production you don't have to worry about this since if you call exit,
|
In production you don't have to worry about this since if you call `exit()`,
|
||||||
immedietely after you destroyied the pool, the threads will be freed
|
immediately after you destroyed the pool, the threads will be freed
|
||||||
anyway by the OS. If you eitherway destroy the pool in the middle of your
|
anyway by the OS. If you anyway destroy the pool in the middle of your
|
||||||
program it doesn't matter again since the program will not exit immediately
|
program it doesn't matter again since the program will not exit immediately
|
||||||
and thus threads will have more than enough time to terminate.
|
and thus threads will have more than enough time to terminate.
|
||||||
|
|
||||||
|
|
||||||
|
### Why does `wait()` use all my CPU?
|
||||||
|
|
||||||
###Why does wait() use all my CPU?
|
Notice: As of 11-Dec-2015 `wait()` doesn't use polling anymore. Instead a conditional variable is being used so in theory there should not be any CPU overhead.
|
||||||
Notice: As of 11-Dec-2015 wait() doesn't use polling anymore. Instead a conditional variable is being used so in theory there should not be any CPU overhead.
|
|
||||||
|
|
||||||
Normally `wait()` will spike CPU usage to full when called. This is normal as long as it doesn't last for more than 1 second. The reason this happens is that `wait()` goes through various phases of polling (what is called smart polling).
|
Normally `wait()` will spike CPU usage to full when called. This is normal as long as it doesn't last for more than 1 second. The reason this happens is that `wait()` goes through various phases of polling (what is called smart polling).
|
||||||
|
|
||||||
* Initially there is no interval between polling and hence the 100% use of your CPU.
|
* Initially there is no interval between polling and hence the 100% use of your CPU.
|
||||||
* After that the polling interval grows exponentially.
|
* After that the polling interval grows exponentially.
|
||||||
* Finally after x seconds, if there is still work, polling falls back to a very big interval.
|
* Finally after x seconds, if there is still work, polling falls back to a very big interval.
|
||||||
|
|
||||||
The reason `wait()` works in this way, is that the function is mostly used when someone wants to wait for some calculation to finish. So if the calculation is assumed to take a long time then we don't want to poll too often. Still we want to poll fast in case the calculation is a simple one. To solve these two problems, this seemingly awkward behaviour is present.
|
The reason `wait()` works in this way, is that the function is mostly used when someone wants to wait for some calculation to finish. So if the calculation is assumed to take a long time then we don't want to poll too often. Still we want to poll fast in case the calculation is a simple one. To solve these two problems, this seemingly awkward behaviour is present.
|
||||||
|
17
example.c
17
example.c
@ -13,16 +13,11 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include "thpool.h"
|
#include "thpool.h"
|
||||||
|
|
||||||
|
void task(void *arg){
|
||||||
void task1(){
|
printf("Thread #%u working on %d\n", (int)pthread_self(), (int) arg);
|
||||||
printf("Thread #%u working on task1\n", (int)pthread_self());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void task2(){
|
|
||||||
printf("Thread #%u working on task2\n", (int)pthread_self());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -33,11 +28,11 @@ int main(){
|
|||||||
|
|
||||||
puts("Adding 40 tasks to threadpool");
|
puts("Adding 40 tasks to threadpool");
|
||||||
int i;
|
int i;
|
||||||
for (i=0; i<20; i++){
|
for (i=0; i<40; i++){
|
||||||
thpool_add_work(thpool, task1, NULL);
|
thpool_add_work(thpool, task, (void*)(uintptr_t)i);
|
||||||
thpool_add_work(thpool, task2, NULL);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
thpool_wait(thpool);
|
||||||
puts("Killing threadpool");
|
puts("Killing threadpool");
|
||||||
thpool_destroy(thpool);
|
thpool_destroy(thpool);
|
||||||
|
|
||||||
|
@ -62,7 +62,12 @@ test_thread_free 8
|
|||||||
test_thread_free 1
|
test_thread_free 1
|
||||||
test_thread_free 20
|
test_thread_free 20
|
||||||
test_thread_free_multi 4 20
|
test_thread_free_multi 4 20
|
||||||
test_thread_free_multi 3 1000
|
|
||||||
test_thread_free_multi 100 100
|
# test_thread_free_multi 3 1000 # Takes way too long
|
||||||
|
test_thread_free_multi 3 200
|
||||||
|
|
||||||
|
# test_thread_free_multi 100 100 # Takes way too long
|
||||||
|
test_thread_free_multi 100 20
|
||||||
|
|
||||||
|
|
||||||
echo "No memory leaks"
|
echo "No memory leaks"
|
||||||
|
Loading…
Reference in New Issue
Block a user