├── .gitignore ├── C++_Coding_Standard └── README.md ├── LICENSE ├── README.md ├── chatroom ├── client.cpp ├── server.cpp └── utility.h ├── memory_pool ├── Makefile ├── MemoryPool.hpp ├── README.md ├── StackAlloc.hpp └── main.cpp ├── stl_memory ├── Makefile ├── README.md ├── main ├── main.cpp └── stl_memory.hpp └── thread_pool ├── Makefile ├── README.md ├── main.cpp ├── thread_pool.hpp └── threadpool_1.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | #other 35 | *.ai 36 | *.psd 37 | *.swp 38 | .* 39 | main 40 | -------------------------------------------------------------------------------- /C++_Coding_Standard/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangsunyang/Cpp_mini_projects/0b42f783d6aae639c1b0c2a33883a94e127038f7/C++_Coding_Standard/README.md -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 huangsunyang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Cpp Mini Projects 2 | 3 | ### Introduction 4 | Here includes several small C++ projects (within several hundred lines). Till now, the projects include: 5 | - [thread pool](https://github.com/huangsunyang/Cpp_mini_projects/tree/master/thread_pool) 6 | - [memory pool](https://github.com/huangsunyang/Cpp_mini_projects/tree/master/memory_pool) 7 | - [stl memory](https://github.com/huangsunyang/Cpp_mini_projects/tree/master/stl_memory): shared_ptr, unique_ptr, etc 8 | 9 | ### Prerequisites: 10 | - g++ that supports C++11 11 | - Makefile 12 | 13 | ### Information 14 | - E-mail: huangsunyang@126.com 15 | Feel free to contact me if you have some question or advice. 16 | -------------------------------------------------------------------------------- /chatroom/client.cpp: -------------------------------------------------------------------------------- 1 | #include "utility.h" 2 | 3 | 4 | int main() { 5 | struct sockaddr_in serverAddr; 6 | serverAddr.sin_family = PF_INET; 7 | serverAddr.sin_port = htons(SERVER_PORT); 8 | serverAddr.sin_addr.s_addr = inet_addr(SERVER_IP); 9 | 10 | int sock = socket(PF_INET, SOCK_STREAM, 0); 11 | if(sock < 0) { 12 | perror("sock error"); 13 | exit(-1); 14 | } 15 | 16 | if(connect(sock, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) { 17 | perror("connect error"); 18 | exit(-1); 19 | } 20 | 21 | int pipe_fd[2]; 22 | if (pipe(pipe_fd) < 0) { 23 | perror("pipe error"); 24 | exit(-1); 25 | } 26 | 27 | int epfd = epoll_create(EPOLL_SIZE); 28 | if (epfd < 0) { 29 | perror("epfd error"); 30 | exit(-1); 31 | } 32 | 33 | static struct epoll_event events[2]; 34 | 35 | addfd(epfd, sock, true); 36 | addfd(epfd, pipe_fd[0], true); 37 | 38 | bool isClientwork = true; 39 | 40 | char message[BUF_SIZE]; 41 | 42 | int pid = fork(); 43 | if (pid < 0) { 44 | perror("fork error"); 45 | exit(-1); 46 | } else if (pid == 0) { 47 | close(pipe_fd[0]); 48 | std::cout << "Please input 'exit' to exit the chat room" << std::endl; 49 | 50 | while (isClientwork) { 51 | bzero(&message, BUF_SIZE); 52 | fgets(message, BUF_SIZE, stdin); 53 | 54 | if(strncasecmp(message, EXIT, strlen(EXIT)) == 0) { 55 | isClientwork = false; 56 | } else if (write(pipe_fd[1], message, strlen(message) - 1) < 0) { 57 | perror("fork error"); 58 | exit(-1); 59 | } 60 | } 61 | } else { 62 | close(pipe_fd[1]); 63 | 64 | while (isClientwork) { 65 | int epoll_events_count = epoll_wait(epfd, events, 2, -1); 66 | for (int i = 0; i < epoll_events_count; i++) { 67 | bzero(&message, BUF_SIZE); 68 | if (events[i].data.fd == sock) { 69 | int ret = recv(sock, message, BUF_SIZE, 0); 70 | 71 | if (ret == 0) { 72 | std::cout << "Server closed connection " << sock << std::endl; 73 | close(sock); 74 | isClientwork = false; 75 | } else { 76 | std::cout << message << std::endl; 77 | } 78 | } else { 79 | int ret = read(events[i].data.fd, message, BUF_SIZE); 80 | if (ret == 0) { 81 | isClientwork = 0; 82 | } else { 83 | send(sock, message, BUF_SIZE, 0); 84 | } 85 | } 86 | } 87 | } 88 | } 89 | 90 | if (pid) { 91 | close(pipe_fd[0]); 92 | close(sock); 93 | } else { 94 | close(pipe_fd[1]); 95 | } 96 | return 0; 97 | } 98 | -------------------------------------------------------------------------------- /chatroom/server.cpp: -------------------------------------------------------------------------------- 1 | #include "utility.h" 2 | 3 | 4 | int main() { 5 | struct sockaddr_in serverAddr; 6 | serverAddr.sin_family = PF_INET; 7 | serverAddr.sin_port = htons(SERVER_PORT); 8 | serverAddr.sin_addr.s_addr = inet_addr(SERVER_IP); 9 | 10 | int listener = socket(PF_INET, SOCK_STREAM, 0); 11 | if (listener < 0) { 12 | perror("Listener"); 13 | exit(-1); 14 | } 15 | 16 | std::cout << "Listen socket created" << std::endl; 17 | 18 | if (bind(listener, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) { 19 | perror("bind error"); 20 | exit(-1); 21 | } 22 | 23 | int ret = listen(listener, 5); 24 | if (ret < 0) { 25 | perror("listen error"); 26 | exit(-1); 27 | } 28 | 29 | std::cout << "Start to listen" << std::endl; 30 | 31 | int epfd = epoll_create(EPOLL_SIZE); 32 | if (epfd < 0) { 33 | perror("epfd error"); 34 | exit(-1); 35 | } 36 | std::cout << "epoll created. epollfd = " << epfd; 37 | static struct epoll_event events[EPOLL_SIZE]; 38 | 39 | addfd(epfd, listener, true); 40 | 41 | while (true) { 42 | int epoll_events_count = epoll_wait(epfd, events, EPOLL_SIZE, -1); 43 | if (epoll_events_count < 0) { 44 | perror("epoll failure"); 45 | break; 46 | } 47 | 48 | std::cout <<"epoll_enents_count = " << epoll_events_count << std::endl; 49 | for (int i = 0; i < epoll_events_count; i++) { 50 | int sockfd = events[i].data.fd; 51 | 52 | if (sockfd == listener) { 53 | struct sockaddr_in client_address; 54 | socklen_t client_addrLength = sizeof(struct sockaddr_in); 55 | int clientfd = accept(listener, (struct sockaddr*) &client_address, &client_addrLength); 56 | 57 | 58 | std::cout << "client connection from" << inet_ntoa(client_address.sin_addr) << " :" 59 | << ntohs(client_address.sin_port) << "(IP:PORT), clientfd = " << clientfd << std::endl; 60 | 61 | addfd(epfd, clientfd, true); 62 | 63 | clients_list.push_back(clientfd); 64 | 65 | std::cout << "add new clientfd = " << clientfd << " to epoll" << std::endl; 66 | std::cout << "Now there are " << clients_list.size() << "clients in the chat room" << std::endl; 67 | 68 | std::cout << "weoclome message" << std::endl; 69 | char message[BUF_SIZE]; 70 | bzero(message, BUF_SIZE); 71 | sprintf(message, SERVER_WELCOME, clientfd); 72 | 73 | int ret = send(clientfd, message, BUF_SIZE, 0); 74 | if (ret < 0) { 75 | perror("send error"); 76 | exit (-1); 77 | } 78 | } else { 79 | int ret = sendBroadcastmessage(sockfd); 80 | if (ret < 0) { 81 | perror("send error"); 82 | exit (-1); 83 | } 84 | } 85 | } 86 | } 87 | close(listener); 88 | close(epfd); 89 | return 0; 90 | } 91 | 92 | -------------------------------------------------------------------------------- /chatroom/utility.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define SERVER_IP "127.0.0.1" 16 | #define SERVER_PORT 8888 17 | #define EPOLL_SIZE 5000 18 | #define SERVER_MESSAGE "ClientID %d say >> %s" 19 | #define EXIT "EXIT" 20 | #define CAUTION "There is only one int the char room!" 21 | #define BUF_SIZE 0xFFFF 22 | #define SERVER_WELCOME "Welcome you join to the chat room! Your chat ID is: Client #%d" 23 | 24 | std::list clients_list; 25 | 26 | int setnonblocking(int sockfd) { 27 | fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0) | O_NONBLOCK); 28 | return 0; 29 | } 30 | 31 | 32 | void addfd(int epollfd, int fd, bool enable_et) { 33 | struct epoll_event ev; 34 | ev.data.fd = fd; 35 | ev.events = EPOLLIN; 36 | if (enable_et) { 37 | ev.events = EPOLLIN | EPOLLET; 38 | } 39 | epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev); 40 | setnonblocking(fd); 41 | std::cout << "fd added to epoll!\n\n" << std::endl; 42 | } 43 | 44 | int sendBroadcastmessage(int clientfd) { 45 | char buf[BUF_SIZE], message[BUF_SIZE]; 46 | bzero(buf, BUF_SIZE); 47 | bzero(message, BUF_SIZE); 48 | 49 | std::cout << "read from client(client ID = " << clientfd << std::endl; 50 | int len = recv(clientfd, buf, BUF_SIZE, 0); 51 | 52 | if (len == 0) { 53 | close(clientfd); 54 | clients_list.remove(clientfd); 55 | std::cout << "ClientID = " << clientfd << "closed. "; 56 | std::cout << "There are " << clients_list.size() << "client in the chat room" << std::endl; 57 | } else { 58 | if (clients_list.size() == 1) { 59 | send(clientfd, CAUTION, strlen(CAUTION), 0); 60 | return len; 61 | } 62 | 63 | sprintf(message, SERVER_MESSAGE, clientfd, buf); 64 | 65 | for (auto it = clients_list.begin(); it != clients_list.end(); it++) { 66 | if (*it != clientfd) { 67 | if (send(*it, message, BUF_SIZE, 0) < 0) { 68 | perror("error"); 69 | exit(-1); 70 | } 71 | } 72 | } 73 | } 74 | return len; 75 | } 76 | -------------------------------------------------------------------------------- /memory_pool/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean 2 | 3 | run: main 4 | ./main 5 | 6 | main: main.cpp StackAlloc.hpp MemoryPool.hpp 7 | g++ -std=c++11 main.cpp -o main 8 | 9 | clean: 10 | rm -rf *.o main 11 | 12 | -------------------------------------------------------------------------------- /memory_pool/MemoryPool.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __MEMORY_POOL_HPP__ 2 | #define __MEMORY_POOL_HPP__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | template 10 | class MemoryPool: public std::allocator { 11 | public: 12 | using ptr = T*; 13 | 14 | template // an inner class/struct 15 | struct rebind { 16 | using other = MemoryPool; 17 | }; 18 | 19 | //default constructor 20 | MemoryPool() noexcept { 21 | current_block_ = nullptr; 22 | current_slot_ = nullptr; 23 | last_slot_ = nullptr; 24 | free_slots_ = nullptr; 25 | } 26 | 27 | //destructor 28 | ~MemoryPool() noexcept { 29 | slot_ptr_ cur = current_block_; 30 | while (cur != nullptr) { 31 | slot_ptr_ next = cur->next; 32 | operator delete(reinterpret_cast(cur)); 33 | cur = next; 34 | } 35 | } 36 | 37 | ptr allocate(size_t n = 1, const T* hint = 0) { 38 | if (free_slots_ != nullptr) { 39 | ptr result = reinterpret_cast(free_slots_); 40 | free_slots_ = free_slots_->next; 41 | return result; 42 | } else { 43 | if (current_slot_ >= last_slot_) { 44 | data_ptr_ new_block = reinterpret_cast(operator new(Blocksize)); 45 | reinterpret_cast(new_block)->next = current_block_; 46 | current_block_ = reinterpret_cast(new_block); 47 | 48 | data_ptr_ body = new_block + sizeof(slot_ptr_); 49 | uintptr_t result = reinterpret_cast(body); 50 | 51 | size_t body_padding = (alignof(slot_type_) - result) % alignof(slot_type_); 52 | 53 | current_slot_ = reinterpret_cast(body + body_padding); 54 | last_slot_ = reinterpret_cast(new_block + Blocksize - sizeof(slot_type_)); 55 | } 56 | return reinterpret_cast(current_slot_++); 57 | } 58 | } 59 | 60 | void deallocate(ptr p, size_t n = 1) { 61 | if (p != nullptr) { 62 | reinterpret_cast (p)->next = free_slots_; 63 | free_slots_ = reinterpret_cast(p); 64 | } 65 | } 66 | 67 | template 68 | void construct(U * p, Args&&... args) { 69 | new (p) U (std::forward(args)...); 70 | } 71 | 72 | template 73 | void destroy(U * p) { 74 | p->~U(); 75 | } 76 | 77 | private: 78 | union Slot_ { 79 | T element; 80 | Slot_* next; 81 | }; 82 | 83 | using data_ptr_ = char *; 84 | using slot_type_ = Slot_; 85 | using slot_ptr_ = Slot_ *; 86 | 87 | slot_ptr_ current_block_; 88 | slot_ptr_ current_slot_; 89 | slot_ptr_ last_slot_; 90 | slot_ptr_ free_slots_; 91 | 92 | static_assert(Blocksize >= 2 * sizeof(slot_type_), "Block size too small"); 93 | 94 | }; 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /memory_pool/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangsunyang/Cpp_mini_projects/0b42f783d6aae639c1b0c2a33883a94e127038f7/memory_pool/README.md -------------------------------------------------------------------------------- /memory_pool/StackAlloc.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | class StackNode_ { 5 | public: 6 | T data; 7 | StackNode_ * prev; 8 | }; 9 | 10 | template > 11 | class StackAlloc { 12 | public: 13 | typedef StackNode_ Node; 14 | typedef typename Alloc::template rebind::other allocator; 15 | 16 | StackAlloc(): head_(0) {} 17 | ~StackAlloc() { 18 | clear(); 19 | } 20 | 21 | bool empty() { 22 | return head_ == nullptr; 23 | } 24 | 25 | void clear(); 26 | 27 | void push(T element); 28 | 29 | T pop(); 30 | 31 | T top() { 32 | return head_->data; 33 | } 34 | 35 | private: 36 | allocator allocator_; 37 | Node * head_; 38 | }; 39 | 40 | template 41 | void StackAlloc::clear() { 42 | Node * cur = head_; 43 | 44 | while (cur != nullptr) { 45 | Node * temp = cur; 46 | cur = cur->prev; 47 | 48 | allocator_.destroy(temp); 49 | allocator_.deallocate(temp, 1); 50 | } 51 | 52 | head_ = nullptr; 53 | } 54 | 55 | template 56 | void StackAlloc::push(T element) { 57 | Node * new_node = allocator_.allocate(1); 58 | 59 | allocator_.construct(new_node, Node()); 60 | 61 | new_node->data = element; 62 | new_node->prev = head_; 63 | head_ = new_node; 64 | } 65 | 66 | template 67 | T StackAlloc::pop() { 68 | T result = head_->data; 69 | Node * temp = head_; 70 | head_ = head_->prev; 71 | 72 | allocator_.destroy(temp); 73 | allocator_.deallocate(temp, 1); 74 | 75 | return result; 76 | } 77 | -------------------------------------------------------------------------------- /memory_pool/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "MemoryPool.hpp" 7 | #include "StackAlloc.hpp" 8 | 9 | #define ELEMS 1000000 10 | #define REPS 100 11 | 12 | 13 | template 14 | double calc_time() { 15 | clock_t start; 16 | 17 | StackAlloc stack; 18 | start = clock(); 19 | 20 | // frequently push and pop elements on the stack 21 | for (int j = 0; j < REPS; j++) { 22 | assert(stack.empty()); 23 | 24 | for (int i = 0; i < ELEMS; i++) { 25 | stack.push(i); 26 | } 27 | for (int i = 0; i < ELEMS; i++) { 28 | stack.pop(); 29 | } 30 | } 31 | 32 | clock_t now = clock(); 33 | return double(now - start) / CLOCKS_PER_SEC; 34 | } 35 | 36 | 37 | int main() { 38 | double time_used; 39 | clock_t start; 40 | 41 | // use default allocator 42 | time_used = calc_time>(); 43 | std::cout << "Default Allocator Time: "; 44 | std::cout << time_used << '\n' << std::endl; 45 | 46 | // use customized memory pool 47 | time_used = calc_time>(); 48 | std::cout << "Using Memory Pool Time: "; 49 | std::cout << time_used << '\n' << std::endl; 50 | 51 | // use vector 52 | start = clock(); 53 | std::vector vec; 54 | for (int j = 0; j < REPS; j++) { 55 | assert(vec.empty()); 56 | 57 | for (int i = 0; i < ELEMS; i++) { 58 | vec.push_back(i); 59 | } 60 | for (int i = 0; i < ELEMS; i++) { 61 | vec.pop_back(); 62 | } 63 | } 64 | std::cout << "Using vector Time: "; 65 | std::cout << double(clock() - start) / CLOCKS_PER_SEC << std::endl; 66 | } 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /stl_memory/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean 2 | 3 | run: main 4 | ./main 5 | 6 | main: main.cpp 7 | g++ -std=c++11 -o main main.cpp 8 | 9 | clean: 10 | rm -rf main 11 | -------------------------------------------------------------------------------- /stl_memory/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangsunyang/Cpp_mini_projects/0b42f783d6aae639c1b0c2a33883a94e127038f7/stl_memory/README.md -------------------------------------------------------------------------------- /stl_memory/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangsunyang/Cpp_mini_projects/0b42f783d6aae639c1b0c2a33883a94e127038f7/stl_memory/main -------------------------------------------------------------------------------- /stl_memory/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "stl_memory.hpp" 7 | 8 | class student; 9 | 10 | class course { 11 | public: 12 | course(){} 13 | ~course(){} 14 | void enroll(std::shared_ptr student_ptr); 15 | void print_student_list() const; 16 | private: 17 | std::vector> student_list; 18 | }; 19 | 20 | 21 | class student: public my_enable_shared_from_this { 22 | public: 23 | student() {} 24 | student(const std::string &s): name(s){} 25 | ~student() {} 26 | 27 | void choose_course(course &c) { 28 | c.enroll(shared_from_this()); 29 | } 30 | 31 | virtual std::string get_name() const { 32 | return this->name; 33 | } 34 | private: 35 | std::string name; 36 | }; 37 | 38 | 39 | void course::enroll(std::shared_ptr student_ptr) { 40 | assert(student_ptr != nullptr); 41 | student_list.push_back(student_ptr); 42 | } 43 | 44 | 45 | void course::print_student_list() const { 46 | std::cout << "There are " << student_list.size() << " students in this class" << std::endl; 47 | for (auto student_ptr: student_list) { 48 | std::cout << student_ptr->get_name() << " ptr_count: " << student_ptr.use_count() << std::endl; 49 | } 50 | } 51 | 52 | 53 | int main () { 54 | std::cout << "hello world" << std::endl; 55 | course math; 56 | auto sam = std::make_shared("SAM"); 57 | auto tom = std::make_shared("TOM"); 58 | 59 | std::cout << sam.use_count() << std::endl; 60 | 61 | sam->choose_course(math); 62 | tom->choose_course(math); 63 | 64 | math.print_student_list(); 65 | std::cout << "Program ends" << std::endl; 66 | return 0; 67 | } 68 | 69 | -------------------------------------------------------------------------------- /stl_memory/stl_memory.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __MY_ENABLE_SHARED_FROM_THIS__ 2 | #define __MY_ENABLE_SHARED_FROM_THIS__ 3 | 4 | #include 5 | 6 | template 7 | class my_enable_shared_from_this { 8 | public: 9 | my_enable_shared_from_this(): weak_ptr_() {} 10 | ~my_enable_shared_from_this() {} 11 | 12 | std::shared_ptr shared_from_this() { 13 | return weak_ptr_.lock(); 14 | } 15 | std::shared_ptr shared_from_this() const { 16 | return weak_ptr_.lock(); 17 | } 18 | protected: 19 | my_enable_shared_from_this& operator= (my_enable_shared_from_this const) { 20 | return *this; 21 | } 22 | 23 | private: 24 | std::weak_ptr weak_ptr_; 25 | }; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /thread_pool/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test clean 2 | 3 | run: test 4 | ./test 5 | 6 | test: main.cpp thread_pool.hpp 7 | g++ -std=c++11 main.cpp -o test 8 | 9 | 10 | clean: 11 | rm -rf test *.o 12 | -------------------------------------------------------------------------------- /thread_pool/README.md: -------------------------------------------------------------------------------- 1 | # Thread Pool using C++ 2 | A small project following [实验楼](https://www.shiyanlou.com/courses/565) tutorial. 3 | 4 | ## Getting Started 5 | 6 | ### Prerequisites 7 | 8 | To run this code, you need the following tools: 9 | - gcc (that support C++11) 10 | - make 11 | 12 | ### Installing 13 | 14 | To simply run it, you can just `cd` to the `thread_pool` directory and run: 15 | ``` 16 | make 17 | ``` 18 | 19 | To generate the excutable file but not run it, just run: 20 | ``` 21 | make test 22 | ``` 23 | 24 | and run it with: 25 | ``` 26 | ./test 27 | ``` 28 | 29 | ## Explanation 30 | 31 | ### General idea 32 | ![](threadpool_1.png) 33 | 34 | With the help of the above image, we now get a general idea about what a thread pool is. As for me, I treat it as a number of containers, each of which can accomodate a task and run it simultaneously. As you may ask, why we need a thread pool. Well, if you just know how many task you have, or your computer is quite high-performance, you can just create one thread for each task and wait. Of course, there is a more elegant way like this. You create a certain number(denote as `k`) of threads, and you run at most `k` tasks at one time. 35 | 36 | If you have viewed the code in `threadpool.hpp`, you may know that there are only three main functions: `ThreadPool()`/`~ThreadPool()`/`enqueue(funcname, all its arguments)`. And we will start explaining them one by one. 37 | 38 | ### Construct function 39 | ``` 40 | ThreadPool(size_t threads); 41 | ``` 42 | 43 | The construct function only receive one argument, that is, the number of 'containers' in the thread pool. If you know about thread before, you may think that a thread can run only one function. Then how can we use the same thread for more than one task? Well, if you look closely into the image above, you may find a little difference between the circle in the `task queue` and 'thread pool'. There is a ring 'wrapping' the task. 44 | 45 | The ring means a wapper function. And the ring is what a thread is really running. The ring is the function that is `emplace_back()` into a thread pool. In the ring there is an eternal loop, in which it detects if there is some task in the task queue and run it if there is any. If there is no task, it will wait for a signal. 46 | 47 | 48 | ### Destruct function 49 | 50 | In fact, I think it is quite obvious. Before a ThreadPool objects dies, it has to finish all its tasks. The member variable `stop` shows if the TheadPool is to die. 51 | 52 | 53 | ### Enqueue function 54 | 55 | This is the most difficult function in this project. I'm not sure if I can explain it well. First, let me ask a question. If the task has some return value, how could we get it? The trick here is when you `enqueue` some task, you will get a `future` object. It is like when you go to some restaurant in china, after you order sth., you will get a receipt. And you can then go to get your food by it when the food's ready. The `future` object's funtion is similar, you can get the return value through it when it's ready. Otherwise, you may be blocked. 56 | 57 | If you understand the main idea of the `enqueue` function is to push the task in to the task queue and return a future object, it's quite enough. Besides are all techinal things: You have to transform the `function` type into a `packaged_task` type in order to get the `future` object, and to notify a thread after enqueue. 58 | 59 | ### Other things 60 | I omit the lock in the code. In fact, the `enqueue` and the running of the task contend for the same lock. That is, when all the containers are running tasks, the `enqueue` function will be blocked and it will unblock and notify a random container when some of the container is availabe. 61 | 62 | 63 | ## Author's comment 64 | 65 | I have to admit that I myself is a beginner and maybe sth. is wrong in the article above. Welcome to contact me(email:huangsunyang@126.com) if you have some question or advice. The tutorial is free to use and I recommend you to use 实验楼 since many of the project in this directory is from that website. 66 | -------------------------------------------------------------------------------- /thread_pool/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // main.cpp 3 | // thread_pool 4 | // 5 | // Created by huangsunyang on 5/20/2017. 6 | // Copyright © 2017年 huangsunyang. All rights reserved. 7 | // 8 | 9 | #include "thread_pool.hpp" 10 | 11 | int main(int argc, const char * argv[]) { 12 | ThreadPool pool(4); 13 | std::vector> results; 14 | 15 | for (int i = 0; i < 8; i++) { 16 | results.emplace_back( 17 | pool.enqueue([i] { 18 | std::cout << "hello" << i << std::endl; 19 | std::this_thread::sleep_for(std::chrono::seconds(16 - i * 2)); 20 | std::cout << "world" << i << std::endl; 21 | return std::string("---thread") + std::to_string(i) + std::string( "finished---"); 22 | }) 23 | ); 24 | } 25 | 26 | for (auto && result: results) 27 | std::cout << result.get() << ' '; 28 | std::cout << std::endl; 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /thread_pool/thread_pool.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // thread_pool.hpp 3 | // thread_pool 4 | // 5 | // Created by huangsunyang on 5/20/2017. 6 | // Copyright © 2017年 huangsunyang. All rights reserved. 7 | // 8 | 9 | #ifndef thread_pool_hpp 10 | #define thread_pool_hpp 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | class ThreadPool { 25 | public: 26 | ThreadPool(size_t threads); 27 | ~ThreadPool(); 28 | 29 | template 30 | auto enqueue(F &&f, Args&&... args) 31 | -> std::future::type>; 32 | 33 | private: 34 | std::vector workers; 35 | std::queue> tasks; 36 | 37 | std::mutex queue_mutex; 38 | std::condition_variable condition; 39 | 40 | bool stop; 41 | }; 42 | 43 | inline ThreadPool::ThreadPool(size_t threads): stop(false) { 44 | for (size_t i = 0; i < threads; ++i) { 45 | workers.emplace_back([this] { 46 | for (;;) { 47 | std::function task; 48 | { 49 | std::unique_lock lock(this->queue_mutex); 50 | 51 | this->condition.wait(lock, [this] { 52 | return this->stop || !this->tasks.empty(); 53 | }); 54 | 55 | if (this->stop && this->tasks.empty()) return; 56 | 57 | task = std::move(this->tasks.front()); 58 | this->tasks.pop(); 59 | } 60 | task(); 61 | } 62 | }); 63 | } 64 | } 65 | 66 | inline ThreadPool::~ThreadPool() { 67 | { 68 | std::unique_lock lock(queue_mutex); 69 | stop = true; 70 | } 71 | 72 | condition.notify_all(); 73 | 74 | for (std::thread &worker: workers) 75 | worker.join(); 76 | } 77 | 78 | 79 | template 80 | auto ThreadPool::enqueue(F&& f, Args&&... args) 81 | ->std::future::type> { 82 | 83 | using return_type = typename std::result_of::type; 84 | 85 | auto task = std::make_shared> 86 | (std::bind(std::forward(f), std::forward(args)...)); 87 | 88 | std::future res = task->get_future(); 89 | 90 | { 91 | std::unique_lock lock(queue_mutex); 92 | 93 | if (stop) 94 | throw std::runtime_error("enqueue on stopped ThreadPool"); 95 | 96 | tasks.emplace([task]{(*task)();}); 97 | } 98 | 99 | condition.notify_one(); 100 | return res; 101 | 102 | } 103 | 104 | #endif /* thread_pool_hpp */ 105 | -------------------------------------------------------------------------------- /thread_pool/threadpool_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangsunyang/Cpp_mini_projects/0b42f783d6aae639c1b0c2a33883a94e127038f7/thread_pool/threadpool_1.png --------------------------------------------------------------------------------