我正在尝试学习如何为学校项目创建多线程程序。我在调试我的项目时遇到了问题。它可能看起来像一堆垃圾,所以我提前道歉。我的编程技能不是很强,这就是我很难找到问题的原因。任何方向都很感激!
午餐c:
#include "mytime.h"
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#define MAX 50
int ticketCounter = 1;
int maxCustomer, maxServer, nowServing, currServer;
sem_t empty, full; // for buffer
sem_t servers;
pthread_mutex_t getTicketLock, waitTurnLock, waitCustomerLock, buffLock;
typedef struct LUNCH {
long int TID;
int ticketNum;
int numServers;
unsigned int takeALooksy;
sem_t ready;
pthread_mutex_t lock;
} lunch;
lunch *orderList;
int listCount = 0, producer = 0, consumer = 0;
void lunch_init();
int lunch_get_ticket(lunch *cust);
void lunch_wait_turn(lunch *cust);
void lunch_wait_customer(lunch *cust); // server waits for a cust to appear
void ordersUp(lunch cust); // add customer to orderList
void consume();
void show_serving(int num);
void *customer(void *threadId);
void *server(void *threadId);
int main(int argc, char **argv) {
if (argc != 3) {
fprintf(stderr, "Usage: %s <total number of servers> <total number of customers>\n", argv[0]);
exit(1);
}
srand(time(NULL)); // for random number seed
maxServer = atoi(argv[1]);
maxCustomer = atoi(argv[2]);
currServer = maxServer;
lunch_init();
lunch Lunch;
pthread_t custThreads[maxCustomer], serverThreads[maxServer];
for (int i = 0; i < maxCustomer; i++) {
if ((pthread_create(&(custThreads[i]), NULL, customer, &Lunch)) != 0) {
perror("customer pthread_create() error\n");
exit(1);
}
}
for (int i = 0; i < maxServer; i++) {
if ((pthread_create(&(serverThreads[i]), NULL, server, &Lunch)) != 0) {
perror("server pthread_create() error\n");
exit(1);
}
}
for (int i = 0; i < maxCustomer; i++) {
pthread_join(custThreads[i], NULL);
}
for (int i = 0; i < maxServer; i++) {
pthread_cancel(serverThreads[i]);
}
return 0;
}
void lunch_init() {
orderList = (lunch *)malloc(MAX * sizeof(lunch));
if (orderList == NULL) {printf("orderList allocation error!\n"); exit(1);}
for (int i = 0; i < MAX; i++) {
orderList[i].ticketNum = -1;
orderList[i].TID = -1;
orderList[i].numServers = maxServer;
orderList[i].takeALooksy = 0;
sem_init(&(orderList[i].ready), 0, 1); // bin semaphore
pthread_mutex_init(&(orderList[i].lock), NULL);
}
sem_init(&servers, 0, maxServer);
sem_init(&empty, 0, MAX);
sem_init(&full, 0, 0);
pthread_mutex_init(&getTicketLock, NULL);
pthread_mutex_init(&waitTurnLock, NULL);
pthread_mutex_init(&waitCustomerLock, NULL);
pthread_mutex_init(&buffLock, NULL);
}
int lunch_get_ticket(lunch *cust) {
printf("<%ld> enter <lunch_get_ticket>\n", pthread_self());
//critical section begins
pthread_mutex_lock(&getTicketLock);
cust->ticketNum = ticketCounter++;
printf("<%ld> get ticket <%d>\n", pthread_self(), cust->ticketNum);
//critical section ends
pthread_mutex_unlock(&getTicketLock);
printf("<%ld> leave <lunch_get_ticket>\n", pthread_self());
return cust->ticketNum;
}
void lunch_wait_turn(lunch *cust) { // customer gets stuck
printf("<%ld> enter <lunch_wait_turn>\n", pthread_self());
pthread_mutex_lock(&waitTurnLock);
// wait for turn without busy waiting
sem_wait(&servers);
pthread_mutex_unlock(&waitTurnLock);
printf("<%ld> leave <lunch_wait_turn> after ticket <%d> served\n", pthread_self(), orderList[consumer - 1].ticketNum);
}
void lunch_wait_customer(lunch *cust) {
printf("<%ld> enter <lunch_wait_customer>\n", pthread_self());
pthread_mutex_lock(&cust->lock);
if (currServer > 0) {
nowServing++;
currServer--;
sem_wait(&(cust->ready));
show_serving(nowServing);
sem_post(&(servers)); // changed to servers from cust->ready
printf("<%ld> after served ticket <%d>\n", pthread_self(), orderList[consumer].ticketNum);
}
// sem_wait(&(cust->ready));
pthread_mutex_unlock(&(cust->lock));
printf("<%ld> leave <lunch_wait_customer>\n", pthread_self());
if (currServer > 0) consume();
}
void ordersUp(lunch customer){
while(1) {
sem_wait(&empty); // wait for an empty slot in the buffer
pthread_mutex_lock(&buffLock); // only one can add to the buffer at a time
orderList[producer] = customer;
listCount++;
producer = (producer + listCount) % MAX; // circ array
pthread_mutex_unlock(&buffLock);
sem_post(&full);
}
}
void consume() {
pthread_mutex_lock(&buffLock);
sem_wait(&full); // wait for something to consume
listCount--;
consumer = (consumer + listCount) % MAX;
pthread_mutex_unlock(&buffLock);
sem_post(&empty);
}
void show_serving(int num) { printf("Serving <%d>\n", num); }
void *customer(void *threadId) {
lunch *temp = (lunch *)threadId;
temp->TID = (long int)pthread_self();
temp->ticketNum = lunch_get_ticket(temp);
temp->takeALooksy = mytime(1, (rand() % 10) + 1);
printf("Sleeping Time: %d sec; Thread Id = %ld\n", temp->takeALooksy, pthread_self());
ordersUp(*temp); // ??
sleep(temp->takeALooksy);
sem_post(&temp->ready);
lunch_wait_turn(temp);
return NULL;
}
void *server(void *threadId) {
lunch *temp = (lunch *)threadId;
sem_post(&servers);
while (1) { // is this busy-waiting?
lunch_wait_customer(temp);
sleep(rand() % 5 + 1);
}
return NULL;
}
mytime文件是由我的导师提供的。
mytime.c:
#include <stdio.h>
#include <stdlib.h>
int mytime (int left, int right)
{
int time = 0;
time = left + rand()%(right - left);
// printf("random time is %d sec\n", time);
return time;
}
mytime.h:
int mytime (int left, int right); // return a random time in the range of [left, right]. Default values are [left, right].
整个错误:__pthread_tpp_change_priority:Assert`new_prio == -1||(new_prio〉= fifo_min_prio && new_prio〈= fifo_max_prio)'失败。
以下是项目描述:你去你最喜欢的餐馆吃午饭。在等待的过程中,你观察了他们的服务流程,并认为你可以编写一个程序来模拟它。你观察到的过程是这样的:一位顾客先拿了一张票,在随机环顾四周一段时间后,客户准备订购,并且票号显示在显示“正在服务”的屏幕上。订单由正在添加的新服务器处理(参见附加要求)。否则,客户等待订单被处理。另一方面,当没有客户时,服务器必须等待。
注意:在本项目中,您将编写代码来使用锁和信号量的同步原语模拟上述过程。它们在C中的pthread库中可用。仅使用锁和信号量,即不允许使用条件变量或其他同步原语。
为了运行模拟器,客户和服务器的总数应该从命令行输入。如果输入错误,提示重试的格式要正确,随机时间的睡眠功能要用来模拟客户取票后四处张望的时间。随机时间的睡眠函数也应该用来模拟服务器等待服务当前客户的时间。
提示:你可以定义一个结构struct lunch,加上下面描述的四个函数。struct lunch应该包含每个客户端所需的信息,如TID,ticket #,并在函数中用于传递信息。你可以使用一个地球仪ticket #计数器。此外,应该使用一个顺序列表来存储提交的ticket。这样,订单将按照先到先得的原则处理,您可以根据自己的喜好来实现列表,如果您使用循环数组来实现,则缓冲区大小可以固定为50作为MAX。
(1)结构午餐的初始化函数,调用一次,从没有客户和服务器开始。
void lunch_init(struct lunch *lunch)
(2)当一个新客户到来时,他调用该函数
int lunch_get_ticket(struct lunch *lunch)
这个函数将返回客户的票号,这个票号同样也在lunch变量中,它是没有返回给任何以前的客户的最小正整数(第一个客户为1,第二个客户为2,以此类推).没有两个客户会收到相同的票号.队列可以在struct lunch内.拿到一张票后客户可以随意查看(通过随机睡眠模拟)。2此功能需要以下输出。
<ThreadId Customer> enter <function name>
<ThreadId Customer> get ticket <ticket number>
<ThreadId Customer> leave <function name>
(3)最终,客户将调用该函数
lunch_wait_turn(struct lunch *lunch)该函数必须在有可用的服务器并且“Now Serving”屏幕显示客户端的票号后才能返回。一旦lunch_wait_turn返回,客户端将由服务器提供服务(无需实现此机制)。以下输出将被打印出来。
<ThreadId Customer> enter <function name> with <ticket number>
<ThreadId Customer> leave <function name> after ticket < ticket number> served.
(4)当一个新的服务器到达时,他将调用该函数
lunch_wait_customer(struct lunch *lunch)
在有客户需要服务之前,它不能返回。顺序列表有助于确保客户端以先到先服务的方式获得服务。一旦此函数返回,服务器将为客户提供服务(您不需要实现该机制)。该函数的输出是:
<ThreadId server > enter <function name>
<ThreadId server > after served ticket < ticket number>
<ThreadId server > leave <function name>
要求:(i)别忘了写一个函数Show_serving(int number),它会使number显示在“正在上菜”屏幕中。在代码中根据需要调用这个函数。输出格式:“服务“
(ii)你的解决方案不能使用busy-waiting,例如,在while循环中等待。
附加要求:(a)从命令行读取服务器的数量和客户端的数量。一台服务器只服务一次。在主程序中,不要忘记加入客户端和服务器。
(b)每种类型的线程使用一个数组,也是一个循环,用一个随机数睡眠。客户端只有在得到票后才睡眠。服务器在服务客户端之前先睡眠。确保在使用随机睡眠持续时间时,设置一个短的睡眠时间。在睡眠之前,打印出“睡眠时间:X秒;线程ID = ThreadId”。
(c)需要调用特定的函数mytime()(参见文件mytime. c,mytime. h)来获取一个随机时间作为sleep函数的输入。
(d)您可以在任何合适的地方使用更多的函数。打印输出应遵循相同的格式。
(e)启动多个线程的例子在早期的帖子中给出,文件名:PC-inputs-main.c
1条答案
按热度按时间zvms9eto1#
初始化
我不知 prop 体是什么原因造成的:
整个错误:__pthread_tpp_change_priority:Assert`new_prio == -1||(新优先级〉=先进先出最小优先级且新优先级〈=先进先出最大优先级)'失败。
我倾向于猜测,这是由于数据竞争和/或使用具有不确定值的对象而引起的程序未定义行为的表现,我将在下面讨论,但我对这个错误消息没有更多要说的。
概述
我不能说我对这个问题的具体措辞有多少看法。它包含了一些令人困惑的条款,其中一些似乎完全不一致;它包含了一些似乎有点奇怪的建议;它包含了一些不标准的,甚至是完全错误的措辞。
让我们从这里开始:
客户总数和服务器总数应通过命令行输入,如果输入错误,应提示重试,格式正确。
您似乎已经部分解决了这个问题,因为您查看命令行参数以获取客户和服务器的数量,如果提供了错误数量的命令行参数,则发出诊断并终止。这也是我解释需求的方式,但我注意到 * prompt * 的指令如果自变量不正确,则可以暗示期望程序改为采用交互式输入。
在任何情况下,如果参数不对应于整数怎么办?或者如果它们对应于 * 非正 * 整数怎么办?
atoi()
函数使用简单,但它不提供任何错误检查。我建议使用strtol()
,这将允许您验证整个参数是否成功解析为整数,并在之后检查结果是否均大于或等于1。接下来吸引我眼球的是:
仅使用锁和信号量,即不允许使用条件变量或其他同步原语。
目前还不清楚分配的“锁”是什么意思。最有可能的是 * 互斥锁 *,但pthreads也有读/写锁,实际上它们的名字中有“锁”。我假设你会理解分配的意思,因为你在课堂上并遵循了文本和讲座。
这个提示似乎包含了很多真正的要求,而且有点不一致。
struct lunch
应包含每个客户端所需的信息但事实上
[应该有]结构午餐的初始化函数,调用一次,[当]没有客户和服务器时。
似乎表明程序的每次运行只有一个
struct lunch
,在线程之间共享,因此一个对象必须包含所有客户所需的信息。您似乎已经做好了准备。1然而,由于该对象由多个线程访问,并且其中一些访问是写,因此在第一个附加线程启动后发生的对它的访问需要同步-例如,仅在锁定指定用于该目的的互斥体时执行。此外,在该对象中存储特定于单个线程的任何内容都没有意义,例如线程ID。
您已经在
struct lunch
中提供了一个互斥锁,我猜它就是为此目的而设计的,但您从未初始化或使用过它。lunch_init()
说到初始化,你已经偏离了
lunch_init()
函数的赋值规范。它应该接受一个指向struct lunch
的指针,并初始化该指针所指向的结构。相反,你没有接受任何参数,并初始化各种全局变量。而且你从来没有初始化你在main()
中创建的struct lunch
。根据分配,
lunch_init()
应该具有以下签名:它应该做的事情包括:
因此,您可以从
main()
使用它,如下所示:当任何线程访问
struct lunch
的成员时,除了同步对象(如lock
),该线程必须首先锁定互斥体:线程应该确保互斥锁的锁定时间不超过必要的时间,但是什么是必要的取决于线程正在做什么。
现在简单介绍一下其他推荐的功能:
lunch_get_ticket()
您的实现看起来基本上没问题。我注意到您使用的是一个特定于保护午餐最新票号的互斥锁,而不是
struct lunch
本身中的互斥锁。如果您喜欢,您可以这样做,并且有一些优点,但如果您这样做,那么我建议将互斥锁移动到struct lunch
中。然而,这里有一个问题。就是这个函数在释放互斥锁之后读取lunch的ticketNum
,它不能这样做,而是在释放互斥锁之前把ticketNum
复制到一个局部变量,然后返回局部变量的值:lunch_wait_turn()和lunch_wait_customer()
这些函数应该一起工作,将客户与服务器配对。这是练习的棘手部分。您的实现至少有以下问题:
lunch_wait_turn()
是目前实现的,waitTurnLock
没有任何用处。它只保护对sem_wait()
的调用,而sem_wait()
不需要这样的保护。lunch_wait_customer()
打印的“Now serving”票号与实际服务的客户票号之间没有相关性(这在输出中很明显,因为客户订单将通过随机等待进行随机化,而“Now serving”票号将按顺序增加)。除非你想为此引入一个全局变量(不要),否则客户和服务器线程只有
struct lunch
的成员用于通信,所以这就是需要交换票号的地方。模式可能需要沿着以下方式进行:servers
信号量),然后进行阻止。请注意,一次只能允许一台服务器进入,以免多台服务器获得同一张票据。一个简单的(-ish)方法是
*客户将其票号写入午餐,然后发布到信号量(即您的
servers
)。*客户然后等待一个不同的信号量(这可能是您的
lunch.ready
,但这将是一个完全不同的用途比它现在提供)。*服务器每个到达并等待信号量门控它们的访问。
然而,请注意,这并不一定提供先到先服务的语义,因为pthreads互斥锁通常不保证FIFO排序。这在赋值中有描述,但我不清楚它是否是一个实际的需求。如果它是一个需求,那么这是不幸的,因为没有好的方法来实现它而不需要忙等待或条件变量。这两种方法都不允许你使用。但是一个坏的方法可能涉及到一个信号量的循环队列。赋值建议50个槽就足够了。一个线程将通过等待队列中下一个可用的信号量来使自己入队,一个线程将通过发布到队列中第一个正在使用的信号量来使一个等待的线程出列。
摘要
您的代码有一些问题,但它在正确的轨道上。请考虑各种变量和函数参数代表什么,并为它们分配反映这一点的名称。您可能会惊讶地发现,这对跟踪发生的事情有多大帮助。此外,请注意适当的同步,并在修饰之前获得基本的工作(例如使用
ordersUp()
和consume()
)。我有意避免为您编写太多的代码,因为您将从本练习中获得最大的好处是自己解决问题,但我认为并希望这些说明将使您朝着正确的方向前进。
1除了
struct LUNCH
与struct lunch
不一样,无论是否有typedef
。我建议尽可能忽略typedef
关键字的存在,至少在你有更多经验之前。它偶尔很方便,但从来没有必要,而且在我看来,它被过度使用了。