我正在尝试使用线程池对Web服务器进行编程,其中主线程接受连接,将其传递给线程,然后线程处理它。
我有一个每个线程的结构,以及一个用于保存它们的workQueue
struct worker {
pthread_t* thread;
struct queue* workerQueue;
char* busy;
int connfd;
int id;
};
struct queue {
int start;
int end;
int size;
struct worker* workers;
};
主线程设置队列和线程,并通过连接循环
struct queue* workerQueue;
workerQueue = (struct queue*) constructQueue(10);
int j;
int* testParam;
//create workers and put in queue
for(j=0;j<5;j++)
{
struct worker* w = &(workerQueue->workers[j]);
w = (struct worker*)constructWorker(processConnection,testParam, workerQueue,j);
queueAdd(workerQueue,w);
}
connection = accept(fd, (struct sockaddr *) &cliaddr, &cliaddrlen);
puts("got connection\n");
w =(struct worker*) queueRemove(workerQueue);
//w->connfd = connection;
w->busy = "BUSY";
printf("Worker %d has accepted a connection and is %s\n",w->id,w->busy);
使用这两个函数..
struct queue* constructQueue(int numThreads)
{
struct queue* q = (struct queue *)malloc(sizeof(struct queue));
q->start = 0;
q->end = 0;
q->workers = (struct worker* )malloc(sizeof(struct worker)*numThreads);
q->size = numThreads;
return q;
}
struct worker* constructWorker(void* (*function)(void*),void* param, struct queue* wq, int i)
{
struct worker* w = (struct worker*)malloc(sizeof(struct worker));
w->workerQueue = wq;
char * busy = (char*)malloc(10);
w->busy= "IDLE";
w->connfd = 0;
w->id = i;
pthread_t t;
w->thread = &t;
pthread_create(w->thread,NULL,function,w);
return w;
}
...和线程使用的函数是
void* processConnection(void* serverThread)
{
//cast serverthread
struct worker* w;
char* b;
int threadID;
w = (struct worker*)serverThread;
b = w->busy;
threadID = w->id;
while (1)
{
char c[10];
printf("\nbusy: %s, thread: %d\n",b,threadID);
gets(c)
我想要发生的是:工作人员被创建,忙着设置为IDLE,并开始忙着等待。然后在主循环中,接受连接并将其分配给worker,并将workers忙值设置为BUSY。然后在processConnections中,如果线程忙,它应该实际处理它。问题是,虽然我的队列包含指针而不是值,但是当我更新主线程中的worker时,它似乎不会影响processConnection中worker的值。我可以将busy设置为BUSY并让它在主循环中打印出来,但busy的值在processConnection中始终是IDLE。有什么想法吗?
答案 0 :(得分:1)
您可能没有在另一个线程中看到更新的值,因为线程之间没有同步点。编译器优化和缓存(in)一致性是发生这种情况的两个原因。要保持相同的策略,您需要一个内存屏障。如果您使用的是gcc,最简单的方法是在读取共享数据之前和写入共享数据之后放置__sync_synchronize()
。
您需要解决的另一件事是
pthread_t t;
w->thread = &t;
该函数返回后,t
的内存可能会被重用。您不能获取局部变量的地址,并以超出函数生命周期的方式存储它。正确的做法是在pthread_t
中添加struct worker
字段,并将字段的地址传递给pthread_create
:
pthread_create(&w->thread, ...);
答案 1 :(得分:1)
尝试将busy
的定义更改为
volatile char * busy;
这告诉编译器在代码运行时这个变量的值可以改变,即使代码没有显式访问该变量。
但是,您还有许多其他问题。例如,
char * busy = (char*)malloc(10);
w->busy= "IDLE";
将泄漏由malloc
分配的内存。
不要尝试使用字符串来跟踪状态。改为定义enum {IDLE, BUSY}
并将busy
定义为该类型的变量。
答案 2 :(得分:0)
我建议在工作时添加2个互斥锁,在队列中添加1个互斥锁
struct worker {
pthread_t* thread;
struct queue* workerQueue;
mutex_t QueueMutex;
char* busy;
int connfd;
int id;
};`
struct queue {
int start;
int end;
int size;
mutex_t workermutex;
struct worker* workers;
};
您的代码应该如下所示
创建新套接字时,请锁定workermutex,然后分配连接
工作线程每次锁定QueueMutex并添加/删除队列中的数据以进行处理。