2015-09-16 81 views
4

现在据我所知,互斥体用于同步所有共享相同数据的线程,遵循的原则是当一个线程正在使用该数据时,所有其他线程应该在使用该公共资源时被阻塞,直到它被解锁为止。现在最近在一篇博客文章中,我看到了一段代码来解释这个概念,有些人写道,在一个线程访问资源时阻塞所有线程是一个非常糟糕的主意,它违背了线程的概念,这在某种程度上是真的。然后我的问题是如何在不阻塞的情况下同步线程?如何在不阻塞的情况下同步线程?

这里是没有通过同步的定义本身阻塞您不能同步线程该博文

http://www.thegeekstuff.com/2012/05/c-mutex-examples/

+2

Mutexes不会阻塞所有线程;它们只会阻塞尝试获取互斥锁的线程。 –

+1

这就是我的意思... –

+0

反思你的应用程序的设计? – alk

回答

2

的链接。但是,良好的同步技术会将事物被阻塞的范围限制在绝对最小值。为了说明这一点,并指出正是为什么文章是错误的考虑以下几点:

从文章:

pthread_t tid[2]; 
int counter; 
pthread_mutex_t lock; 

void* doSomeThing(void *arg) 
{ 
    pthread_mutex_lock(&lock); 

    unsigned long i = 0; 
    counter += 1; 
    printf("\n Job %d started\n", counter); 

    for(i=0; i<(0xFFFFFFFF);i++); 

    printf("\n Job %d finished\n", counter); 

    pthread_mutex_unlock(&lock); 

    return NULL; 
} 

它应该是什么:

pthread_t tid[2]; 
int counter; 
pthread_mutex_t lock; 

void* doSomeThing(void *arg) 
{ 
    unsigned long i = 0; 

    pthread_mutex_lock(&lock); 
    counter += 1; 
    int myJobNumber = counter; 
    pthread_mutex_unlock(&lock); 

    printf("\n Job %d started\n", myJobNumber); 

    for(i=0; i<(0xFFFFFFFF);i++); 

    printf("\n Job %d finished\n", myJobNumber); 

    return NULL; 
} 

注意的是,在文章中,正在完成的工作(毫无意义的循环)是在按住锁的情况下完成的。这完全是无稽之谈,因为这项工作应该同时完成。需要锁的原因只是为了保护counter变量。因此,线程只需在第二个示例中更改该变量时保持锁定。如果试图在同一访问临界区和所有其他线程必须块 -

互斥锁保护关键部分的代码,这是只有1在时间线应该触及这些代码领域时间。但是,如果线程1位于临界区域,而线程2不在,那么两者同时运行就完全正常了。

+0

这里你不需要互斥锁。这意味着,您可以在不使用互斥锁的情况下实现相同的功能,并且可能没有好处。但至少你应该提到它。 – SergeyA

+0

@SergeyA我试图解释所讨论的文章出错的地方,按照我理解的问题的意图,而不是重写文章。如果你有建设性的补充,请提交你自己的答案。 –

+0

@JimWood,您在代码中的更正是错误的,“counter”是线程之间的共享变量,并且printf使用该变量,它必须被锁定。因为这是正确的当前作业计数器应存放在一个局部变量解锁计数器,以便工作(for循环),可以正确地并行和打印右反转 ... 的pthread_mutex_lock(&lock); 柜台前+ = 1; INT AUX =计数器; 调用pthread_mutex_unlock(&lock); 的printf( “\ n作业%d开始\ n” 个,AUX); 为(I = 0; I <(0xFFFFFFFF的);我++); 的printf(“\ n Job%d完成\ n“,aux); return NULL; } –

0

您可以使用pthread_mutex_trylock()来尝试锁定。如果失败,那么你知道你已经阻止。你不能做你想做的事,但你的线程没有被阻塞,所以它可以尝试做别的事情。我认为该博客上的大部分评论都是关于避免线程之间的争用,即最大化多线程性能是为了避免线程同时在同一资源上工作。如果您通过设计避免了这种情况,那么通过设计您就不需要锁,因为您从来没有争用过。

+0

我正在设计一个TCP/IP服务器wh如果我创建一个单独的线程,它将从客户端接收数据并发送到其他客户端,但如果没有数据由该客户端发送,则客户端将与服务器进行通信以向对方发送数据。服务于具有'read'系统调用的客户端的服务器线程将会阻塞,直到它再次接收到数据..在那里我怎么能使用trylock ..或者任何其他机制,这将是高效和并发的 –

+1

使用trylock时要小心 - - 如果资源有争议,一些线程永远不会有机会运行。基本上最响亮的线程(经常尝试的线程)将占主导地位。例如,如果你有读者和作者,你可以得到一个有效的死锁或缓冲区溢出,而无需等待互斥体。 :) – Eugene

+0

阅读并不一定要阻止,如果你不想要它...... – Joe

1

您要查找的术语是lock free data structures

总的想法是线程之间共享的状态被扭曲成其中之一。

这些实现不尽相同,通常是编译器或平台特定的。例如MSVC有一组_Interlocked*函数来执行简单的原子操作。

+0

请注意,这些通常比相应的锁定数据结构执行得更差,所以它们很少被使用。但是,如果您碰巧遇到了表现更好的罕见情况之一,有时好得多,它们可能非常有价值。 –

0

有一些技巧可以用来避免并发瓶颈。

  1. 不可变数据结构。这里的想法是并发读取没问题,但写入不是。要实现这样的事情,基本上需要将业务单位视为工厂,以应用于其他业务部门使用的不可变数据结构。
  2. 异步回调。这是事件驱动开发的本质。如果您有并发任务,则在资源可用时使用观察者模式执行某些逻辑。基本上我们执行一些代码直到需要共享资源,然后为资源何时可用添加监听器。这通常会导致较少的可读代码和堆栈上的压力,但永远不会阻塞等待资源的线程。如果您已准备好使CPU保持热点的任务,则此模式将为您执行此操作。

即使使用这些工具,您也永远不会完全删除某些同步(需要记住计数器)的需要。

+0

你如何想象事件发生器和监听器进行通信?例如,如果侦听器在事件生成器排队事件的同一时刻检查消息,为什么您认为不会阻塞? –

+0

我想你是对的,线程可能会在排队任务时短暂阻塞。 –

1

阻塞所有线程,而一个线程访问的资源是一个非常糟糕的主意,它违背线程的概念,它是真的不知何故

这是一个谬论。锁只阻塞竞争线程,允许所有非竞争线程同时运行。运行在任何特定时间运行效率最高的工作,而不是强制执行任何特定的排序,这根本不符合线程的概念。

现在,如果这么多的线程争得很厉害,阻断争线程是损害性能,有两种可能性:

  1. 最有可能你有一个非常糟糕的设计,你应该修复它。不要责怪锁定高争用设计。

  2. 在极少数情况下,其他同步机制更合适(例如无锁集合)。但是这需要重要的专业知识和对特定用例的分析才能找到最佳解决方案。

一般来说,如果您的用例是完美适合原子,请使用它们。否则,互斥体(可能与条件变量结合)应该是你的第一个想法。这将覆盖典型的多线程C程序员将面临的99%的情况。

相关问题