我有一个小的线程池示例供学习使用:
它需要工作结构,它代表两个值的简单计算(+, - ,*,/)。 现在,工作线程将把这些工作结构拉出队列并执行它们。
这个理论太多了。现在的问题是,当我想使用辅助函数submit_work()时,工作线程中的队列解析不起作用(拉出的数据无效,当我使用QUEUE_REMOVE时,它会发生分段错误。)
如何通过使用辅助功能,整个概念不再起作用了?
pool.c
我的意思是手动推送数据工作正常...
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include "../queue/queue.h"
#define MAX_THREADS 1
pthread_t threads[MAX_THREADS];
pthread_cond_t cond;
pthread_mutex_t mutex;
/**
* QUEUE is a "void * arr[2]".
* The Queue itself is similar to the circularly linked list in linux
*/
QUEUE queue;
struct work_s {
int a;
int b;
int type;
QUEUE node;
};
void * worker();
void submit_work(int a, int b, int type);
int main() {
QUEUE_INIT(&queue);
pthread_cond_init(&cond, NULL);
pthread_mutex_init(&mutex, NULL);
struct work_s work[2];
/* 5 + 7 */
work[0].a = 5;
work[0].b = 7;
work[0].type = 1;
/* 3 x 3 */
work[1].a = 3;
work[1].b = 3;
work[1].type = 3;
/* initialize there queue nodes */
QUEUE_INIT(&work[0].node);
QUEUE_INIT(&work[1].node);
/* insert both tasks into the work queue */
QUEUE_INSERT_TAIL(&queue, &work[0].node);
QUEUE_INSERT_TAIL(&queue, &work[1].node);
/* this does actually the same as above but causes a segmentation fault. */
submit_work(5, 6, 3);
for (int i = 0; i < MAX_THREADS; i++)
pthread_create(&threads[i], NULL, worker, NULL);
for (int i = 0; i < MAX_THREADS; i++)
pthread_join(threads[i], NULL);
for (int i = 0; i < MAX_THREADS; i++)
pthread_detach(threads[i]);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
void submit_work(int a, int b, int type) {
struct work_s work;
work.a = a;
work.b = b;
work.type = type;
pthread_mutex_lock(&mutex);
QUEUE_INIT(&work.node);
QUEUE_INSERT_TAIL(&queue, &work.node);
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond);
}
void * worker() {
/* a pointer to a queue node */
QUEUE * q;
int result;
struct work_s * work;
/* infinite loop */
for (;;) {
while (QUEUE_EMPTY(&queue)) {
pthread_cond_wait(&cond, &mutex);
}
pthread_mutex_lock(&mutex);
q = QUEUE_HEAD(&queue);
/* HERE THE SEGMENTSTION FAULT OCCURS when using submit_work */
QUEUE_REMOVE(q);
pthread_mutex_unlock(&mutex);
/* set the work pointer to the work struct we have pulled from queue */
work = QUEUE_DATA(q, struct work_s, node);
/* PRINTS INCORRECT DATA on submit_work() */
printf("received work type %d with a %d and b %d \n", work->a, work->b, work->type);
if (work->type == 0) {
break;
}
switch (work->type) {
case 1:
result = work->a + work->b;
printf("%d + %d = %d\n", work->a, work->b, result);
break;
case 2:
result = work->a - work->b;
printf("%d - %d = %d\n", work->a, work->b, result);
break;
case 3:
result = work->a * work->b;
printf("%d * %d = %d\n", work->a, work->b, result);
break;
case 4:
result = work->a / work->b;
printf("%d / %d = %d\n", work->a, work->b, result);
break;
}
}
pthread_exit(NULL);
}
queue.h
/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef QUEUE_H_
#define QUEUE_H_
typedef void *QUEUE[2];
/* Private macros. */
#define QUEUE_NEXT(q) ((*(q))[0])
#define QUEUE_PREV(q) ((*(q))[1])
#define QUEUE_PREV_NEXT(q) (QUEUE_NEXT((QUEUE *) QUEUE_PREV(q)))
#define QUEUE_NEXT_PREV(q) (QUEUE_PREV((QUEUE *) QUEUE_NEXT(q)))
/* Public macros. */
#define QUEUE_DATA(ptr, type, field) \
((type *) ((char *) (ptr) - ((long) &((type *) 0)->field)))
#define QUEUE_FOREACH(q, h) \
for ((q) = (*(h))[0]; (q) != (h); (q) = (*(q))[0])
#define QUEUE_EMPTY(q) \
(QUEUE_NEXT(q) == (q))
#define QUEUE_HEAD(q) \
(QUEUE_NEXT(q))
#define QUEUE_INIT(q) \
do { \
QUEUE_NEXT(q) = (q); \
QUEUE_PREV(q) = (q); \
} \
while (0)
#define QUEUE_ADD(h, n) \
do { \
QUEUE_PREV_NEXT(h) = QUEUE_NEXT(n); \
QUEUE_NEXT_PREV(n) = QUEUE_PREV(h); \
QUEUE_PREV(h) = QUEUE_PREV(n); \
QUEUE_PREV_NEXT(h) = (h); \
} \
while (0)
#define QUEUE_SPLIT(h, q, n) \
do { \
QUEUE_PREV(n) = QUEUE_PREV(h); \
QUEUE_PREV_NEXT(n) = (n); \
QUEUE_NEXT(n) = (q); \
QUEUE_PREV(h) = QUEUE_PREV(q); \
QUEUE_PREV_NEXT(h) = (h); \
QUEUE_PREV(q) = (n); \
} \
while (0)
#define QUEUE_INSERT_HEAD(h, q) \
do { \
QUEUE_NEXT(q) = QUEUE_NEXT(h); \
QUEUE_PREV(q) = (h); \
QUEUE_NEXT_PREV(q) = (q); \
QUEUE_NEXT(h) = (q); \
} \
while (0)
#define QUEUE_INSERT_TAIL(h, q) \
do { \
QUEUE_NEXT(q) = (h); \
QUEUE_PREV(q) = QUEUE_PREV(h); \
QUEUE_PREV_NEXT(q) = (q); \
QUEUE_PREV(h) = (q); \
} \
while (0)
#define QUEUE_REMOVE(q) \
do { \
QUEUE_PREV_NEXT(q) = QUEUE_NEXT(q); \
QUEUE_NEXT_PREV(q) = QUEUE_PREV(q); \
} \
while (0)
#endif /* QUEUE_H_ */
如果我还要复制QUEUE.h文件发表评论。否则你会发现here
博多
答案 0 :(得分:3)
问题是您正在将submit_work()
的局部变量插入到队列中(队列例程不会复制它们正在排队的对象)。因此,一旦submit_work()
返回,排队的结构中的数据就不再有效(并且可能会将链接指针更改为垃圾)。
排队的前两个项目不会发生这种情况,因为在main()
调用中pthread_join()
阻止后,线程函数执行时它们的生命周期仍然有效。
您应该对动态分配的项进行排队,以便它们的生命周期延伸到排队它们的函数的lfe之后。当然,那么你还需要具有使项目出列的函数释放内存。当然,这意味着您还应该动态分配由main()
直接排队的项目。
答案 1 :(得分:2)
您在worker()
中执行此循环而不保留互斥锁:
while (QUEUE_EMPTY(&queue)) {
pthread_cond_wait(&cond, &mutex);
}
pthread_cond_wait()
只能在持有传递给它的互斥锁时被调用 - 它也会在锁定该互斥锁的情况下返回,所以你不应该在该循环之后立即调用pthread_mutex_lock(&mutex)
- 它已被锁定
所以代码应该类似于:
pthread_mutex_lock(&mutex);
while (QUEUE_EMPTY(&queue)) {
pthread_cond_wait(&cond, &mutex);
}
// don't call pthread_mutex_lock(&mutex) here...