在回答带有多线程的c++密码破解器时,我最后用C编写了一个示例,这不是我的强项。
有什么是我遗漏的,应该包含在一个负责任的样本中(读:可能被复制并粘贴到生产代码中)?
// compile with `gcc counter.c -o counter -lpthread -Wall -std=gnu99`
#include <pthread.h>
#include <stdio.h>
#include <stdbool.h>
#include <limits.h>
#define SEARCH_THREAD_COUNT (10)
#define SEARCH_VALUE (21325)
#define SEARCH_MAX (INT_MAX)
typedef struct {
int start;
int end;
int search;
bool* shouldStop;
int* answer;
pthread_mutex_t* answerMutex;
pthread_cond_t* answerFound;
pthread_t thisThread;
} find_worker_init;
void* find_value(void* vpInitInfo) {
find_worker_init* pUnitOfWork = (find_worker_init*)vpInitInfo;
int cValue = pUnitOfWork->start;
while (!(*(pUnitOfWork->shouldStop))) {
if (cValue == pUnitOfWork->search) {
printf("found value\n");
// found the search value
pthread_mutex_lock(pUnitOfWork->answerMutex);
if (!(*(pUnitOfWork->shouldStop))) {
*(pUnitOfWork->shouldStop) = true;
*(pUnitOfWork->answer) = cValue;
pthread_cond_broadcast(pUnitOfWork->answerFound);
}
pthread_mutex_unlock(pUnitOfWork->answerMutex);
return NULL;
}
cValue++;
if (cValue == pUnitOfWork->end) {
// we exhausted our search space, end.
printf("exhausted\n");
return NULL;
}
}
// we were usurped by another thread
printf("usurped\n");
return NULL;
}
int main( int argc, const char* argv[] ) {
pthread_mutex_t answerMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t answerFound = PTHREAD_COND_INITIALIZER;
bool shouldStop = false;
int answer = -1;
// initialize thread jobs
find_worker_init startInfo[SEARCH_THREAD_COUNT];
int current_search_start = 0;
int search_unit = SEARCH_MAX / SEARCH_THREAD_COUNT;
for (int i = 0; i < SEARCH_THREAD_COUNT; i++) {
startInfo[i].start = current_search_start;
// set the search space for this thread to be either the standard
// search space, or for the last thread, whatever is remaining
// (this is to prevent integer-division from skipping a portion
// at the end)
int current_size = (i == SEARCH_THREAD_COUNT - 1)
? (SEARCH_MAX - current_search_start)
: search_unit;
startInfo[i].end = current_search_start + current_size;
startInfo[i].search = SEARCH_VALUE;
startInfo[i].shouldStop = &shouldStop;
startInfo[i].answer = &answer;
startInfo[i].answerMutex = &answerMutex;
startInfo[i].answerFound = &answerFound;
}
// start threads
for (int i = 0; i < SEARCH_THREAD_COUNT; i++) {
if (pthread_create(&(startInfo[i].thisThread), NULL, find_value,
&(startInfo[i])))
{
fprintf(stderr, "Error creating thread\n");
return 1;
}
}
// wait for answer to be found
pthread_mutex_lock(&answerMutex);
while (!shouldStop) {
pthread_cond_wait(&answerFound, &answerMutex);
}
printf("signaled\n");
// join
for (int i = 0; i < SEARCH_THREAD_COUNT; i++) {
if (pthread_join(startInfo[i].thisThread, NULL)) {
fprintf(stderr, "Error joining thread\n");
return 1;
}
}
// answer
printf("answer: %d\n", answer);
pthread_mutex_unlock(&answerMutex);
return 0;
}发布于 2014-10-10 11:54:17
除了其他帖子附带的问题(以及额外的括号、不寻常的缩进和camel_casing对我来说“我不习惯使用这种语言”,这不是件坏事,但它确实很突出),您的代码有一个大问题。
您对shouldStop所指向的值的读取违反了p线程标准。具体来说,这部分:http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_11
“应用程序应确保由多个控制线程(线程或进程)对任何内存位置的访问受到限制,以便控制线程不能读取或修改内存位置,而另一个控制线程则可以修改该位置。”
这不仅仅是为了避免许多人似乎认为的种族状况。如果您希望您的线程代码是完全可移植的,则不能假设它将运行在缓存一致的体系结构上。这意味着,如果不调用其中一个锁定函数(或其中提到的其他内存同步函数),您就不能假设您将看到修改过的shouldStop值。
这实际上使得停止工作线程变得非常困难。如果您有一个执行许多廉价操作的紧循环,那么在每一个循环步骤上锁定的开销都会太高。基本上,您最终会在锁上被竞争,并序列化整个程序,并使线程浪费时间。甚至因为内存一致性协议是如何在大多数CPU上工作的,甚至rwlocks也会导致序列化。
要停止工作线程,最好的办法是在执行“足够”的工作单元之后,用锁定检查该条件。为一些手调值“足够”。
你也可以使用pthread_cancel,但是一旦你开始做一些复杂的事情,就很难纠正它。一旦启用异步传递取消,您就必须在分配资源或接受锁(以避免资源泄漏)的代码部分中禁用它,您的代码最终会导致对pthread_setcancelstate的无休止的调用混乱。
另一种选择是使用来自<stdatomic.h>的C11。即使它与线程的交互并不是真正标准化的,它也应该能工作。
或者像世界上大多数人一样忽略这个问题。因为那些在没有缓存一致性的体系结构上运行的人已经习惯了在他们的系统上什么都不做。
https://codereview.stackexchange.com/questions/64839
复制相似问题