我想在这里分享一下我在多道程序设计方面的实践经验。
昨天我写了一个多节目。对可共享资源的修改放在受P(互斥)和V(互斥)保护的关键部分下,并将那些关键部分代码放在一个公共库中。该库将由并发应用程序(我自己的)使用。
我有三个应用程序将使用库中的公共代码并独立完成它们的工作。
my library
---------
work_on_shared_resource
{
P(mutex)
get_shared_resource
work_with_it
V(mutex)
}
---------
my application
-----------
application1
{
*[
work_on_shared_resource
do_something_else_non_ctitical
]
}
application2
{
*[
work_on_shared_resource
do_something_else_non_ctitical
]
}
application3
{
*[
work_on_shared_resource
]
}
*[...] denote a loop.
------------
我必须在Linux OS上运行应用程序。多年来,我心里想着,操作系统应该公平地安排在他下面运行的所有进程。换句话说,它将为所有流程提供资源使用方式。
当前两个应用程序投入使用时,它们运行良好,没有死锁。但是当第三个应用程序开始运行时,第三个应用程序总是获得资源,但由于它在非关键区域没有执行任何操作,因此当其他任务执行其他任务时,它会更频繁地获取共享资源。因此发现其他两个应用程序几乎完全停止。当第三个应用程序被强制终止时,前两个应用程序恢复了以前的工作。
我认为,这是一个饥饿的案例,前两个应用程序不得不饿死。
现在我们如何确保公平?
现在我开始相信操作系统调度程序是无辜和盲目的。这取决于谁赢得了比赛;他获得了最大的CPU和资源。
我们是否应该尝试在库中的关键部分代码中确保资源用户的公平性?
或者我们是否应该通过自由而非贪婪的方式将其留给申请以确保公平?
据我所知,添加代码以确保公共图书馆的公平性将是一项艰巨的任务。另一方面,相信应用程序也永远不会确保100%的公平性。在使用共享资源之后执行一项非常小的任务的应用程序将赢得竞争,因为在共享资源工作之后执行繁重处理的应用程序应该总是饿死。
在这种情况下,最佳做法是什么?我们在哪里确保公平和如何?
此致
Srinivas Nayak
答案 0 :(得分:2)
出现问题的原因是“应用程序3”进程在解锁互斥锁后继续运行,因此可以立即再次获取它。
您可以实现一个公平的排队系统,其中每个线程在阻塞时都会添加到队列中,并且队列中的第一个线程在资源可用时始终获取该资源。您可以使用条件变量来构建它。在pthreads原语上构建的这种“公平”票证锁可能如下所示:
#include <pthread.h>
typedef struct ticket_lock {
pthread_cond_t cond;
pthread_mutex_t mutex;
unsigned long queue_head, queue_tail;
} ticket_lock_t;
#define TICKET_LOCK_INITIALIZER { PTHREAD_COND_INITIALIZER, PTHREAD_MUTEX_INITIALIZER }
void ticket_lock(ticket_lock_t *ticket)
{
unsigned long queue_me;
pthread_mutex_lock(&ticket->mutex);
queue_me = ticket->queue_tail++;
while (queue_me != ticket->queue_head)
{
pthread_cond_wait(&ticket->cond, &ticket->mutex);
}
pthread_mutex_unlock(&ticket->mutex);
}
void ticket_unlock(ticket_lock_t *ticket)
{
pthread_mutex_lock(&ticket->mutex);
ticket->queue_head++;
pthread_cond_broadcast(&ticket->cond);
pthread_mutex_unlock(&ticket->mutex);
}
答案 1 :(得分:1)
已经进行了关于撤销互斥锁所有权以确保平等共享或检测并避免死锁的研究。然而,这实在是太过分了,并不值得付出努力,特别是因为它开辟了一整套新的蠕虫,例如程序在某些计算过程中应该如何表现并失去其资源的所有权。这完全取决于应用程序,以确保其各种线程相互公平,并以不会陷入僵局的方式消耗资源。
我应该指出,还有另一种选择,虽然种类略有不同,这就是使用并行编程的消息传递方式。通过消息传递,同步在通信中都是隐含的;不需要显式同步,这有助于避免此类错误。