contributed by <carolc0708
>
作業說明 A06: phonebook-concurrent
alloc()
的時間成本
fgets()
會 blocking I/Oblocking I/O & non-blocking I/O
Blocking IO means that a given thread cannot do anything more until the IO is fully received (in the case of sockets this wait could be a long time).
For blocking IO you either need to accept that you are going to wait for every IO request or you are going to need to fire off a thread per request (Which will get very complicated very quickly).
Non-blocking IO means an IO request is queued straight away and the function returns. The actual IO is then processed at some later pointer by the kernel.
For non-blocking IO you can send off multiple requests but you need to bear in mind that the data will not be available until some "later" point. This checking that the data has actually arrived is probably the most complicated part.
這樣一來,即使將程式改為 multithread 版本也無法保證效能提升。 故使用 mmap() 先將記憶體內容映射到對應的記憶體空間,做快速的記憶體存取。
MMAP(2) Linux Programmer's Manual MMAP(2)
NAME
mmap, munmap - map or unmap files or devices into memory
SYNOPSIS
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length);
DESCRIPTION
mmap() creates a new mapping in the virtual address space of the calling process. The starting address for the new mapping is specified in addr. The length argument specifies the length of the mapping.
If addr is NULL, then the kernel chooses the address at which to create the mapping; this is the most portable method of creating a new mapping. If addr is not NULL, then the kernel takes it as a hint about where to place the mapping; on Linux, the mapping will be created at a nearby page boundary. The address of the new mapping is returned as the result of the call.
The contents of a file mapping (as opposed to an anonymous mapping; see MAP_ANONYMOUS below), are initialized using length bytes starting at offset offset in the file (or other object) referred to by the file descriptor fd. offset must be a multiple of the page size as returned by sysconf(_SC_PAGE_SIZE).
munmap()
The munmap() system call deletes the mappings for the specified address range, and causes further references to addresses within the range to generate invalid memory references. The region is also automatically unmapped when the process is terminated. On the other hand, closing the file descriptor does not unmap the region.
The address addr must be a multiple of the page size. All pages containing a part of the indicated range are unmapped, and subsequent references to these pages will generate SIGSEGV. It is not an error if the indicated range does not contain any mapped pages.
int dprintf(int fd, const char *format, ...);
The function dprintf() is the same as fprintf(3) except that it outputs to a file descriptor, fd, instead of to a stdio stream.
NAME
pthread_setconcurrency, pthread_getconcurrency - set/get the concurrency level
SYNOPSIS
#include <pthread.h>
int pthread_setconcurrency(int new_level);
int pthread_getconcurrency(void);
Compile and link with -pthread.
DESCRIPTION
The pthread_setconcurrency() function informs the implementation of the application's desired concurrency level, specified in new_level. The implementation takes this only as a hint: POSIX.1 does not specify the level of concurrency that should be provided as a result of calling pthread_setconcurrency().
Specifying new_level as 0 instructs the implementation to manage the concurrency level as it deems appropriate.
pthread_getconcurrency() returns the current value of the concurrency level for this process.
RETURN VALUE
On success, pthread_setconcurrency() returns 0; on error, it returns a nonzero error number.
pthread_getconcurrency() always succeeds, returning the concurrency level set by a previous call to pthread_setconcurrency(), or 0, if pthread_setconcur‐
rency() has not previously been called.
ERRORS
pthread_setconcurrency() can fail with the following error:
EINVAL new_level is negative.
POSIX.1 also documents an EAGAIN error ("the value specified by new_level would cause a system resource to be exceeded").
VERSIONS
These functions are available in glibc since version 2.1.
ATTRIBUTES
For an explanation of the terms used in this section, see attributes(7).
┌──────────────────────────┬───────────────┬─────────┐
│Interface │ Attribute │ Value │
├──────────────────────────┼───────────────┼─────────┤
│pthread_setconcurrency(), │ Thread safety │ MT-Safe │
│pthread_getconcurrency() │ │ │
└──────────────────────────┴───────────────┴─────────┘
pthread_cond_wait() blocks the calling thread until the specified condition is signalled. This routine should be called while mutex is locked, and it will automatically release the mutex while it waits. After signal is received and thread is awakened, mutex will be automatically locked for use by the thread. The programmer is then responsible for unlocking mutex when the thread is finished with it.
Recommendation: Using a WHILE loop instead of an IF statement (see watch_count routine in example below) to check the waited for condition can help deal with several potential problems, such as:
The pthread_cond_signal() routine is used to signal (or wake up) another thread which is waiting on the condition variable. It should be called after mutex is locked, and must unlock mutex in order for pthread_cond_wait() routine to complete.
The pthread_cond_broadcast() routine should be used instead of pthread_cond_signal() if more than one thread is in a blocking wait state.
It is a logical error to call pthread_cond_signal() before calling pthread_cond_wait().
Proper locking and unlocking of the associated mutex variable is essential when using these routines. For example:
在此使用 thread_num = 4
, queue_size = 16
,執行時間與沒有 threadpool 的 optimized 版本相比其實沒有差很多。
thread_num = 4
,結果 append() 執行時間暴增。