我希望所有线程都从同一个结构中读取。我在过去通过在循环中添加从结构中读取的线程来做到这一点,但这次我需要在void“dowork”内部打开结构,如我的示例所示。
我有以下代码:
struct word_list {
char word[20];
struct word_list * next;
};
struct word_list * first_word = NULL;
//other function which loads into struct is missing cause it's not relevant
//in main()
pthread_t thread_id[MAX_THREADS];
int max_thread = 10;
for(t = 0 ; t < max_thread; t++)
{
pthread_mutex_lock(&thrd_list);
arg_struct *args = calloc(1, sizeof(*args));
args->file = file;
args->t = t;
args->e = ex;
pthread_mutex_unlock(&thrd_list);
if(pthread_create(&thread_id[t],NULL,dowork,args) != 0)
{
t--;
fprintf(stderr,RED "\nError in creating thread\n" NONE);
}
}
for(t = 0 ; t < max_thread; t++)
if(pthread_join(thread_id[t],NULL) != 0)
{
fprintf(stderr,RED "\nError in joining thread\n" NONE);
}
void *dowork(void *arguments)
{
struct word_list * curr_word = first_word;
char myword[20];
while( curr_word != NULL )
{
pthread_mutex_lock(&thrd_list);
strncpy(myword,curr_word->word,sizeof(myword) - 1);
pthread_mutex_unlock(&thrd_list);
//some irrelevant code is missing
pthread_mutex_lock(&thrd_list);
curr_word = curr_word->next;
pthread_mutex_unlock(&thrd_list);
}
}
如何在所有线程中读取同一结构中的不同元素?
答案 0 :(得分:2)
让我看看我是否理解正确?
struct word_list
描述了某种链接列表如果这是你想要的,那么我只是从列表中逐个弹出元素并将指针写回其余部分:
volatile struct word_list * first_word = NULL; // important to make it volatile
void *dowork(void *arguments)
{
struct word_list * curr_word;
char myword[20];
do {
// gain exclusive access to the control structures
pthread_mutex_lock(&thrd_list);
// get the next element
curr_word = first_word;
if (curr_word == NULL) {
pthread_mutex_unlock(&thrd_list);
break;
}
// notify the remaining threads what the next element is
first_word = curr_word->next;
pthread_mutex_unlock(&thrd_list);
// do whatever you have to do
} while (1);
}
如果您不想修改volatile struct word_list * next_word
,请另外设置全局first_word
。确保将其设为volatile
,否则编译器可能会执行导致奇怪结果的优化。
答案 1 :(得分:2)
因此,您希望通过跨多个线程拆分工作来处理大量数据。您的解决方案效率不高,因为您的线程将与许多拥有互斥锁的人进行战斗,而您无法确定工作是否均匀分布在所有线程中。因此,例如,线程0和1可以获得所有工作,因为它们首次访问互斥锁,而所有其他线程一直处于空闲状态。
如果您想提高性能,则需要执行以下操作: -
因此,在您的程序中,不是在所有线程中共享的单个链表,而是为每个线程都有一个链表: -
typedef struct _word_list
{
//data
struct _word_list *next;
} word_list;
static const int num_threads = 4; // actually, setting this to number of CPUs at run time would be better
word_list
*lists [num_threads] = {0};
void ReadWords ()
{
word_list
**current [num_threads];
for (int i = 0 ; i < num_threads ; ++i)
{
current = &lists [i];
}
int destination = 0;
while (read some valid input)
{
*current [destination] = malloc (sizeof (word_list));
// set data
current [destination] = ¤t [destination]->next;
destination = (destination + 1) % num_threads;
}
// data has now been read and stored into a series of linked lists, each list having
// the same number of items (or one less)
}
void create_threads ()
{
for (int i = 0 ; i < num_threads ; ++i)
{
// create thread, and pass it the value of lists [i]
}
}
void do_work (...)
{
for (word_list *item = passed in parameter ; item ; item = item->next)
{
process data
}
}
在这个程序中(刚刚完成,没有检查过)我创建了四个链表并将数据均匀地分配给列表。然后我创建线程并给每个线程一个链接列表。然后每个线程处理自己的链表(它们是单独的列表)。
现在每个线程都可以全速运行,而不必等待互斥锁来获取数据。内存访问是合理的,但在很大程度上依赖于分配器。使用数组而不是链表可以改善这一点,但是在分配数组之前你需要知道数据项的数量,这可能是不可能的。
答案 2 :(得分:2)
如果我现在了解您的要求(我想我最终会这样做),您需要将您的单词列表视为工作队列。要做到这一点,需要一种通知机制,允许项目“推动”进入队列,以通知“拉拔器”新数据可用。这样的系统确实存在于pthreads中:条件变量,互斥和它们为控制流管理的谓词的结合
这是如何使用它的一个例子。我试图记录每一步的内容,希望你能理解。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
// defined the number of threads in our queue and the number
// of test items for this demonstration.
#define MAX_THREADS 16
#define MAX_ITEMS 128*1024
typedef struct word_list
{
char word[20];
struct word_list * next;
} word_list;
// predicate values for the word list
struct word_list * first_word = NULL; // current word.
int word_shutdown = 0; // shutdown state
// used for protecting our list.
pthread_mutex_t wq_mtx = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t wq_cv = PTHREAD_COND_INITIALIZER;
// worker proc
void *dowork(void*);
int main()
{
pthread_t thread_id[MAX_THREADS];
int i=0;
// start thread pool
for(i=0; i < MAX_THREADS; ++i)
pthread_create(thread_id+i, NULL, dowork, NULL);
// add MAX_ITEMS more entries, we need to latch since the
// work threads are actively processing the queue as we go.
for (i=0;i<MAX_ITEMS;++i)
{
word_list *node = malloc(sizeof(*node));
sprintf(node->word, "Word-%d", i);
// latch before updating the queue head.
pthread_mutex_lock(&wq_mtx);
node->next = first_word;
first_word = node;
// no longer need the latch. unlock and inform any
// potential waiter.
pthread_mutex_unlock(&wq_mtx);
pthread_cond_signal(&wq_cv);
}
// wait for the condition that the queue is empty
pthread_mutex_lock(&wq_mtx);
while (first_word != NULL)
pthread_cond_wait(&wq_cv, &wq_mtx);
pthread_mutex_unlock(&wq_mtx);
// queue is empty, but threads are all still there waiting. So
// do it again, just to proves the pool is still intact.
for (i=0;i<MAX_ITEMS;++i)
{
word_list *node = malloc(sizeof(*node));
sprintf(node->word, "Word-%d", i);
// latch before updating the queue head.
pthread_mutex_lock(&wq_mtx);
node->next = first_word;
first_word = node;
// no longer need the latch. unlock and inform any
// potential waiter.
pthread_mutex_unlock(&wq_mtx);
pthread_cond_signal(&wq_cv);
}
// again wait for the condition that the queue is empty
pthread_mutex_lock(&wq_mtx);
while (first_word != NULL)
pthread_cond_wait(&wq_cv, &wq_mtx);
// queue is empty, and we're not adding anything else. latch
// the mutex, set the shutdown flag, and tell all the threads.
// they need to terminate.
word_shutdown = 1;
pthread_mutex_unlock(&wq_mtx);
pthread_cond_broadcast(&wq_cv);
for (i=0;i<MAX_THREADS; ++i)
pthread_join(thread_id[i], NULL);
return EXIT_SUCCESS;
}
// the work crew will start by locking the mutex, then entering the
// work loop, looking for entries or a shutdown state
void *dowork(void *arguments)
{
int n_processed = 0;
while (1)
{
pthread_mutex_lock(&wq_mtx);
while (first_word == NULL && word_shutdown == 0)
pthread_cond_wait(&wq_cv, &wq_mtx);
// we own the mutex, and thus current access to the predicate
// values it protects.
if (first_word != NULL)
{
// pull the item off the queue. once we do that we own the
// item, so we can unlatch and let another waiter know there
// may be more data on the queue.
word_list *p = first_word;
first_word = p->next;
if (p->next)
pthread_cond_signal(&wq_cv);
pthread_mutex_unlock(&wq_mtx);
//
// TODO: process item here.
//
++n_processed;
free(p);
}
else if (word_shutdown != 0)
break;
}
// we still own the mutex. report on how many items we received, then
// one more signal to let someone (anyone, actually) know we're done.
pthread_t self = pthread_self();
printf("%p : processed %d items.\n",self, n_processed);
pthread_mutex_unlock(&wq_mtx);
pthread_cond_signal(&wq_cv);
return NULL;
}
示例输出:MAX_THREADS = 4 (您的输出会有所不同)
0x100387000 : processed 64909 items.
0x100304000 : processed 64966 items.
0x1000b5000 : processed 64275 items.
0x100281000 : processed 67994 items.
样本输出:MAX_THREADS = 8
0x100304000 : processed 31595 items.
0x1000b5000 : processed 33663 items.
0x100593000 : processed 34298 items.
0x10040a000 : processed 32304 items.
0x10048d000 : processed 32406 items.
0x100387000 : processed 31878 items.
0x100281000 : processed 32317 items.
0x100510000 : processed 33683 items.
样本输出:MAX_THREADS = 16
0x10079f000 : processed 17239 items.
0x101081000 : processed 16530 items.
0x101104000 : processed 16662 items.
0x100699000 : processed 16562 items.
0x10040a000 : processed 16672 items.
0x100593000 : processed 15158 items.
0x10120a000 : processed 17365 items.
0x101187000 : processed 14184 items.
0x100387000 : processed 16332 items.
0x100616000 : processed 16497 items.
0x100281000 : processed 16632 items.
0x100304000 : processed 16222 items.
0x100510000 : processed 17188 items.
0x10048d000 : processed 15367 items.
0x1000b5000 : processed 16912 items.
0x10071c000 : processed 16622 items.
正因为我们可以,启用全局全局优化
样本输出:MAX_THREADS = 32,MAX_ITEMS = 4194304
0x109c58000 : processed 260000 items.
0x109634000 : processed 263433 items.
0x10973a000 : processed 262125 items.
0x10921c000 : processed 261201 items.
0x108d81000 : processed 262325 items.
0x109a4c000 : processed 262318 items.
0x108f8d000 : processed 263107 items.
0x109010000 : processed 261382 items.
0x109946000 : processed 262299 items.
0x109199000 : processed 261930 items.
0x10929f000 : processed 263506 items.
0x109093000 : processed 262362 items.
0x108e87000 : processed 262069 items.
0x108e04000 : processed 261890 items.
0x109acf000 : processed 261875 items.
0x1097bd000 : processed 262040 items.
0x109840000 : processed 261686 items.
0x1093a5000 : processed 262547 items.
0x109b52000 : processed 261980 items.
0x109428000 : processed 264259 items.
0x108f0a000 : processed 261620 items.
0x1095b1000 : processed 263062 items.
0x1094ab000 : processed 261811 items.
0x1099c9000 : processed 262709 items.
0x109116000 : processed 261628 items.
0x109bd5000 : processed 260905 items.
0x10952e000 : processed 262741 items.
0x1098c3000 : processed 260608 items.
0x109322000 : processed 261970 items.
0x1000b8000 : processed 262061 items.
0x100781000 : processed 262669 items.
0x1096b7000 : processed 262490 items.
嗯。我没有在任何一个中使用volatile
。必须是购买乐透彩票的时间。
无论如何,我建议对pthread进行一些研究,特别是关于互斥和条件变量控制及其交互。我希望这会帮助你。