我要投搞

标签云

收藏小站

爱尚经典语录、名言、句子、散文、日志、唯美图片

当前位置:港彩神鹰 > 读入原语 >

并发操作: Windows Vista 新增的同步原语

归档日期:04-17       文本归类:读入原语      文章编辑:爱尚语录

  条件变量在其他线程库中已经存在一段时间了,只是很遗憾地被 Windows SDK 遗漏了。条件变量主要用来根据一些条件测试的结果同步化一组线程。尽管可通过使用现有同步结构组合来做到这一点,但是条件变量具有释放已获取的锁,并通过一个原子操作进入休眠状态的能力。它还提供了一种更清楚且又少出错的方法,以用于实现所需的行为。

  Windows SDK for Windows Vista(可供下载)公开了 CONDITION_VARIABLE 结构的条件变量。您可用 InitializeConditionVariable 函数创建该结构。没有用来清理或销毁 CONDITION_VARIABLE 结构的函数,因为底层实现用不着它。

  通过使用函数 SleepConditionVariableCS(使用关键节时)或 SleepConditionVariableSRW(使用 Slim 读取器锁/写入器锁时),您可让线程等待条件变量。当另一条线程调用 WakeConditionVariable 或 WakeAllConditionVariable 时,这些休眠线程将被释放,这取决于调用线程是想要释放等待条件变量的一条线程还是所有线程。

  常见的生产者/使用者问题代表了可使用条件变量的情况。这一典型示例是指生产者生成数据并将其置入缓冲区,而使用者则从缓冲区抓取待处理的数据片段的情形。该问题指出了一种需求,即保证生产者不会试图向填满的缓冲区添加数据,而使用者不会试图从空缓冲区抓取数据。我们将分析本情形,以向您说明条件变量是如何帮助解决问题的。

  针对此例,我们将创建一个向共享队列传送数值数据的单一生产者线程。我们然后创建五个使用者线程。每个使用者线程将从队列中转移一个项目并进行处理。当处理完当前数据段后,使用者线程将循环,无限重复该过程。

  在早期版本的 Windows 中,可用 Win32 事件和关键节组合来解决生产者/使用者问题。当资源可供使用者使用时,关键节会保护共享资源、避免出现并发性访问和事件信号。

  在我们首次尝试解决这个问题时,我们将标准模板库 (Standard Template Library, STL) 整数列表用作共享资源。由于列表会动态扩展,我们无需使用事件以信号形式通知列表何时是未填满的,我们只需了解它何时不是空的,这样使用者就能知道其中有内容可供使用。(如果您打算使用固定大小的数组来容纳共享队列,则需要一个未满事件,以确保您不会向缓冲区写入过多内容。)我们随后声明并初始化 CRITICAL_SECTION 对象以及用于说明列表何时不为空的自动重置事件。

  所示的生产者线程将首先尝试获取关键节,并且如果获取成功,将随后在共享列表的末尾插入一个整数值。该线程然后释放关键节并设置非空事件。因为我们在使用一个自动重置事件,因此只释放一个等待本事件的线

  所示的使用者线程将查看队列是否为空。如果队列不为空,该线程将转移一个项目并释放关键节。如果队列为空,使用者线程将返回休眠状态,继续等待非空事件。在第一个使用者线程忙于处理它从队列转移的项目时,生产者将唤醒另一个使用者线程拾取下一段工作以确保队列处于移动状态。

  尽管本解决方案通常是可行的,但对本实现也有一些局限。比如说,正确初始化同步对象就是个难题。例如,我们必须决定在将数据推入列表时,生产者应该只唤醒一个使用者还是唤醒所有使用者。这可通过我们初始化事件的方法来控制。如果我们使用自动重置事件,就只能释放一个线程。但如果我们想要唤醒所有线程,我们将使用手动重置事件,并且必须记住在正确的时间调用 ResetEvent。

  必须确保当队列为空时,使用者线程在等待非空事件之前释放关键节。我们还必须确保不要错误使用生产者线程中的 PulseEvent 来发送非空事件信号,因为这样会导致争用状况。如果在使用者线程刚刚释放关键节、尚未调用 WaitForSingleObject 时被抢占,并且生产者随即调用 PulseEvent,就会出现这个问题。PulseEvent 并非“粘滞的”(sticky),它将只释放目前正在等待事件的那些线程。当被抢占的线程恢复时,将不会发送事件信号,并且将丢失唤醒。

  使用条件变量将更容易获得正确的解决方案。本方法仍然使用关键节,但它用条件变量取代非空事件。我们通过调用指向我们的 CONDITION_VARIABLE 结构的 InitializeConditionVariable,来初始化主函数内的条件变量。

  所示使用者线程进入关键节并查看队列是否为空。如果为空,则调用 SleepConditionVariableCS。此函数会释放关键节,并通过一个原子操作让此线程进入休眠状态,从而避免在两个任务间隙使用者被抢占时可能出现的争用情况。SleepConditionVariableCS 函数还接受一个超时参数,从而让我们在不想等得太久时,可以做别的工作。

  当得到条件变量信号时,该线程醒来并在 SleepConditionVariableCS 函数返回之前再次自动锁定关键节。使用者线程然后往回循环,并再次查看队列是否为空。当线程被释放后,再次检测状态非常重要,因为其状态可能在使用者被释放之前已被另一个线程更改;另外,条件变量还可能被骗醒 (spurious wakeup),这样它可能会在条件发生变化之前运行。我们随后从队列中转移工作项并释放关键节。

  所示生产者线程通过进入关键节启动。一旦有了锁,生产者线程就会将一个新工作项推入队列,然后释放关键节。现在它可通过调用 WakeConditionVariable 释放使用者线程。此函数仅释放一个线程,这与不使用条件变量的解决方案一样。如果我们想释放所有使用者线程,我们将调用 WakeAllConditionVariable 函数。条件变量语法可以清楚显示出发送线程的过程,因为函数名称说明了将要做的事情。使用事件会使这一过程更容易出错,原因是所需行为并不是用事件信号函数而是初始化事件的时间来指定的。

  如果生产者线程在它刚刚释放关键节但尚未调用 Wake 函数之前被抢占,就为另一个线程在使用者线程被释放之前修改队列状态提供了缺口。这正是我们说的释放条件时,使用者线程必须对其进行再次检查的原因。

  条件变量在某些情况中也可能更高效。SleepConditionVariableCS 和 SleepConditionVariableSRW API 会尽可能候试图避免进入内核模式的行程。但不使用条件变量的示例在调用 WaitForSingleObject 时,总是会发生至少一次内核模式往返。

  这样,与自己开发的实现 (homegrown implementation) 相比,本机条件变量实现可带来诸多好处,并且不会降低灵活性。生成的代码更简单、更容易阅读,并且性能也可提高。这大大降低了导致许多难以发现的细微错误的可能性。

  读取器锁/写入器锁用于保护您想允许多个读取器并发访问,但在更新时只允许写入器访问的一段数据。通常,这些锁最适用于需要频繁读取和更新数据的情形。恰当使用读取器锁/写入器锁有利于增加可伸缩性。

  读取器锁/写入器锁出现已有一段时间了。但在 Windows Vista 发布之前,如果您在 Windows 中编写本机用户模式代码并且需要读取器锁/写入器锁,您唯一的选择是编写自己的锁或修改教科书上的实现。Windows Vista 包含了一种称为 Slim 读取器锁/写入器锁 (SRW) 的读取器锁/写入器锁。让我们来看看这个新同步原语的功能集,测试一下可用来使用它的 API,并与用 Win32

  SRW 锁的设计初衷是快速、小型(如其名),同时仍能保持使用时的资源效率。它以 Windows 内核键控事件机制为基础生成,引入此机制是为了解决应用程序使用大量 CRITICAL_SECTION 对象时可能发生的资源匮乏问题。有些读取器锁/写入器锁旨在使读取器优先于写入器,反之亦然。但 SRW 锁旨在不偏向任何一方。也就是说,如果您的应用程序要求数据更新的优先级高于数据读取,则最好考虑选用偏向写入器的另一种读取器锁/写入器锁。但在编写自己的锁之前,您最好先试试 SRW 锁,看看它在您的应用程序环境中表现如何。

  读取器锁/写入器锁有时支持的其他两项功能分别是递归获得锁,以及升级(或降级)线程被授予的锁访问权。首先,关于递归获得:如果您为应用程序设计的锁策略要求递归获得同步目标,很可能出现一个红色标志,提示您重新检查锁策略以清除递归。这是我们的看法,是由于多次执行锁获得和释放代码所带来的额外系统开销而造成的,或许还有一个更重要的原因,那就是确保锁释放和锁获得之间的平衡通常是很难证明是正确的。

  SRW 锁不支持递归获得。此支持会造成额外系统开销,原因是为了维持准确性需进行逐线程的计数。SRW 锁也不支持从共享访问升级到独占访问,反之也不支持从独占访问降级(较少见)到共享访问。支持升级能力可能会造成难以接受的复杂性和额外系统开销,这种开销甚至会影响锁内共享和独占获得代码的常见情况。它还要求定义关于如何选择等待中的读取器、等待中的写入器和等待升级的读取器的策略,这又将与无偏向的基本设计目标相抵触。

  使用 Slim 读取器锁/写入器锁的第一步是声明 SRWLOCK 结构并用 InitializeSRWLock 进行初始化:

  VOID WINAPI InitializeSRWLock(PSRWLOCK SRWLock);

  SRW 锁摆脱了每个对象均有一个初始化和清理函数的 Win32 惯例模式。当您结束使用 SRW 锁时,无需调用清理函数。

  VOID WINAPI AcquireSRWLockExclusive(PSRWLOCK SRWLock); VOID WINAPI ReleaseSRWLockExclusive(PSRWLOCK SRWLock); VOID WINAPI AcquireSRWLockShared(PSRWLOCK SRWLock); VOID WINAPI ReleaseSRWLockShared(PSRWLOCK SRWLock);

  顾名思义,AcquireSRWLockExclusive 函数用于获得供调用方独占访问的锁。一旦被授予独占访问权,所有要求任一访问类型的其他线程均将被阻止,直到用 ReleaseSRWLockExclusive 互补函数释放该锁。相对的,AcquireSRWLockShared 会以共享访问权获得该锁。此时,如果锁是无主的或者已被其他具有共享访问权的线程获得,同样要求共享访问的其他线程则无需等待。

  Slim 读取器锁/写入器锁可与使用 SleepConditionVariableSRW 函数的条件变量结合使用:

  BOOL WINAPI SleepConditionVariableSRW( PCONDITION_VARIABLE ConditionVariable, PSRWLOCK SRWLock, DWORD dwMilliseconds, ULONG Flags );

  SleepConditionVariableSRW 释放 SRW 锁,并作为原子操作等待特定条件变量。在发送条件变量信号或 dwMilliseconds 内指定的超时结束之前,此函数不会返回。如果 dwMilliseconds 为 INFINITE,该函数将永不超时。如果 Flags 参数指定 CONDITION_VARIABLE_LOCKMODE_SHARED,此函数认为 SRW 锁具有共享访问权;否则将认为是独占访问,并在成功返回后,该锁将以指定的访问权被重新获得。

  在试验这些 API 的过程中,我们发现了一些编码时需格外注意的问题。请注意,没有任何锁获得或释放 API 被定义为返回结果的类型。如果 SRW 锁当前没有为一个线程所有,并发生了对 ReleaseSRWLockShared 或 ReleaseSRWLockExclusive 的调用,将会引发一个 STATUS_RESOURCE_NOT_OWNED 结构的异常。这并非坏事,因为开发人员可以明显地看出错误。

  我们以前提到,不支持递归锁获得。再次尝试递归获得独占访问权的结果是,对 AcquireSRWExclusive 的第二次调用将永不返回,原因是它将自动发生死锁,使线程被阻止在内部,只有等待锁被释放。您可能需要附加一个调试器到此过程中,以查看具体情形并了解到底发生了什么问题。如果另一个线程已经尝试在两次 AcquireSRWShared 调用间隙以独占方式获得锁,那么从已获得共享访问权的线程调用 AcquireSRWShared 也将导致死锁。

  在确保使用正确的获得与释放函数对时也要格外注意 — 错误地将 AcquireSRWLockExclusive 与 ReleaseSRWLockShared 配对(或反之亦然)不会引发任何异常。如果已引发针对这些出错情形的异常将很有帮助,但检测错误可能会产生不必要的资源或性能开销。

  本文中的示例代码包括称为 ReaderWriterExample 的程序源代码,它允许针对不同读取器锁/写入器锁实现进行试验。该程序支持的锁类型是 SRW 锁、我们实现的读取器锁/写入器锁的两个变量和关键节。自定义读取器锁/写入器锁是用 Win32 关键节生成的,以保护锁的数据、向等待中的读取器发送信号的事件和用于写入器的信号灯。两种自定义锁的区别在于,一种偏向写入器,另一种既不偏向读取器也不偏向写入器。

  所有这些新奇的测试都是在双核 Intel Xeon 3.20 GHz 处理器系统、64 位的 Windows Vista 平台上进行的。为进行这些测试,系统被配置成禁用超线 包含每个线 个迭代,其中有锁时所做的工作最少。因此,结果可反映出锁获得和释放的实际消耗。总结的结果如

  其中有一些有趣的发现。首先,在只使用一个读取器线程或一个写入器线程(结果的前两列)时,未出现锁竞争,并且 SRW 锁的性能非常接近关键节的性能。在使用四个写入器线程时,所有线程都竞争对锁的独占访问权,SRW 锁的用时仅约占关键节方法所用时间的一半。使用独占模式锁的 SRW 锁的性能似乎优于关键节,并且值得考虑用作替代品。请注意,在标有 2 个读取器/2 个写入器和 3 个读取器/1 个写入器的栏中,SRW 锁比使用关键节的锁速度快很多。这说明了允许数据读取器并行工作的读取器锁/写入器锁的优点。

  关于我们自己开发的读取器锁/写入器锁,我们该作何评价呢?与两个内置 Windows 锁相比,它们的性能似乎很差,尤其是在读取器和写入器争用的重负载下运行时。但请看看 2 个读取器/2 个写入器和 3 个读取器/1 个写入器的结果。请注意,锁策略确实会影响总体行为和性能。偏向写入器的锁速度慢于既不偏向读取器也不偏向写入器的锁。这是因为在写入器等待更新时,并行机制被迫向写入器提供优先权。当然,这么做通常是为了确保读取器能看到最新数据,因此使用偏向写入器的锁在本例中是正确的选择。

  当四个写入器竞争锁时,会出现自定义锁的另一个问题。在出现对独占访问权的许多竞争时,性能为何变得如此之差?问题在于每个线程都在竞争保护内部数据的关键节的访问权。对关键节的竞争导致进入内核模式的行程,线程在其中被置于事件休眠状态。一旦进入关键节,就会检查锁的状态,以查看任何读取器是否具有共享访问权或另一个写入器是否已经具有独占访问权。在任何一种情况下,想得到独占访问权的线程必须离开关键节,然后等待信号灯。要线程具有独占访问权并可继续运行时,将发送上述信号灯。如您可看到的,必须频繁等待多个同步目标以获得独占访问权确实降低了性能。

  ReaderWriterExample 应用程序包括多个命令行开关,这样就可定制它的行为以允许通过使用不同类型的读取器锁/写入器锁来试验不同的方案。受锁保护的数据属于简单的 LONG 类型。然而,应用程序在有锁时允许指定待完成的额外数量的工作,以便模拟访问或更新更复杂的数据结构。可为读取器和写入器单独指定额外工作参数,从而允许数据结构模拟,该结构会因为更新而不是读取而带来更多负担。还有用来指定读取器/写入器在每个锁访问间隙的工作量的参数。通过固定执行计算的循环来模拟工作。

  我们都知道,自己开发的锁在读取器和写入器频繁争用而处于重负载下的性能不是很好。另外,SRW 锁在第一个示例中的所有情况下则都运行良好。这是否意味着既然有了 SRW 锁,您便没必要再构建自己的读取器锁/写入器锁呢?不一定。我们来看看,将锁用于更接近应用程序实际应用方案时会发生什么。

  在这些方案中,读取器的共享访问次数是 1,000,000,而写入器的独占访问次数是 150,000。这与我们前面所说的相符 — 共享访问对独占访问的比例较高时,读取器锁/写入器锁才有意义。另外,读取器拥有对 2000 个工作单元的锁以模拟读取请求,而写入器则拥有 3000 个工作单元的锁以模拟升级数据所带来的额外成本。一个读取器线程在每次访问锁的间隙执行 100 个工作单元,这样访问的时间间隔较短,而写入器在尝试独占访问之前执行 10,000 个工作单元,这样更新的时间间隔就会延长。本方法可减少对锁的整体争用。

  我们使用 2 个读取器/ 2 个写入器和 3 个读取器/1 个写入器来进行本测试。总结的结果如

  所示。表中的数字是每个线程完成其工作所需的时间(以秒为单位)。对于读取器线程,第二个数字是读取器观察到的数据更新次数。

  这些结果显示我们自己开发的锁和 SRW 锁存在更多的相似性。请注意不同的策略是如何影响结果的。偏向写入器的锁会让读取器等待而允许写入器访问,这意味着更新被赋予更高的优先级并相应地允许读取器看到更多更新。在应用程序中赋予更新优先权有时非常重要,因此了解预期负载下的锁策略及其性能特征是需要考虑的重要因素。

  SRW 锁是 Windows 平台上杰出的新同步原语。Windows 首次向本机系统程序员提供内置读取器锁/写入器锁。它在许多不同的环境下均运行良好,并应成为您使用的首选读取器锁/写入器锁。您的应用程序有时可受益于不同的锁策略,但正如我们所演示的,建立适用于许多方案的读取器锁/写入器锁并不那么容易。

  在建立多线程系统时,时不时会出现如何确保正确初始化供多个线程共享的对象或资源的问题。C 和 C++ 并未提供解决这一问题的帮助,原因是语言标准未提及多线程支持。假设这样一个示例,该示例包含一个用于记录消息日志的 Logger 对象实例,其中一个要求是按需进行对象实例化,而不是在开始执行程序时创建对象。当有多个线程在 GetLogger 函数内部同时执行以访问我们系统中的 Logger 对象时,

  Logger* GetLogger() { // C++ will execute the Logger’s constructor // only the first time this method is called. static Logger logger; return }

  静态 Logger 对象的初始化实际上不止发生一次,原因是编译器并未在其结构上加入任何同步。这可能会产生损坏的 Logger 对象,当使用该对象时,即使处于最佳情形,也会产生异常 — 但不能保证错误很明显,并且系统可能运行了很长时间后才会有人发现此问题。解决此问题的一种办法是重新编写函数并引入同步功能,这样它就可利用

  现在,进入 GetLogger 函数的每个线程都将试图进入关键节,即意味着每次只允许一个线程进入受保护的代码块。在关键节执行的线程会检查 pLogger 的值,只有值为 NULL 时才会创建一个 Logger 对象的实例。此操作仅在第一个线程进入关键节后会发生一次。随后进入的其他所有线程将发现 pLogger 不是 NULL,然后会退出关键节而不再执行任何工作。到达返回语句时,pLogger 值将是非空值,并可被返回给调用方。

  线程安全初始化代码表面上看似合理的解决方案。但在此必须打个折扣,尤其是当许多线程结束时同时调用 GetLogger 函数的情况下。一旦第一个线程已经完成分配、构建和设置 pLogger 指针,实际上根本不再需要后面的线程进入关键节对象。该指针始终保持有效。这种认识带来了一个称为仔细查过锁定模式的 C++ 编程设计模式。图 7显示了使用仔细查过锁定模式的 GetLogger 实现。此模式规定 pLogger 变量要被检查两次。第一次是检查变量,在关键节之外进行。如果发现 pLogger 为 NULL,线程随后将进入关键节。一旦进入,它将在实例化和设置 pLogger 变量之前再次检查 pLogger 是否为空,原因是等待关键节时由于受另一线程的竞争,该线程可能被阻止。Figure 7 仔细查过锁定复制代码Logger* GetLogger() { volatile static Logger* pLogger = 0; if (pLogger == NULL) { EnterCriticalSection( if (pLogger == NULL) { try { pLogger = new Logger(); } catch (...) { // Something went wrong. } } LeaveCriticalSection( } return pLogger; }看起来仔细查过锁定版本好像能提供世界上最好的解决方案。只有少数几个在对象被实例化之前进入 GetLogger 的线程将被强行同步;以后到达的线程根本无需进入关键节。我们还想再要什么?仔细查过锁定模式尽管在概念上很简单,但事实证明,到目前为止,仍然难以正确编码。这是因为 C++ 标准没有定义线程模型。它假定只有一个执行线程,并且没有定义可供开发人员表示相关指令排序的约束的方式,这使得编译器可以自由重新排序内存的读取和写入。在多线程环境中,重新排序可能会导致线程在实际执行源代码中位置比它更靠前的所有语句之前就观察内存的写入。对于仔细查过锁定代码,可以在执行 Logger 构造函数之前,用分配给 Logger 对象的内存地址更新 pLogger 变量。观察到非空值的第二个线程将不再进入关键节,并返回未完全构造完成的对象的地址。(您可参见 Vance Morrison 撰写的关于托管代码的类似问题的文章“解读多线程应用程序中 Low-Lock 技术的影响”。)在旧版 Visual Studio®

  尽管 Visual Studio 2005 能正确实现仔细查过锁定模式,但还是很容易因为忽略包含实例指针的变量上的 volatile 限制符,或忽略关键节内的检查而使实现出错;而程序仍在编译并且似乎仍然有效。建立保证有效的机制将是对 C++ 有重要的补充。Windows Vista 没有坐等对标准主体的修订,而是提供了一个专门针对这一问题,称为一次性初始化的工具。不管您在哪一种硬件平台上使用 Windows,都能保证有效。一次性初始化既允许同步初始化,也允许异步初始化。让我们先来看看同步初始化。

  在概念上,同步初始化的工作模式与仔细查过锁定模式一样。在第一批 n 个试图同步执行初始化的线程中,实际上只有一个线程将实例化资源 — 其余线程将一直被阻止,直到初始化完成。一旦完成初始化,将不再阻止后面试图访问资源的线程;只会返回存储的资源。

  需要完成的第一件事情是声明并初始化 INIT_ONCE 结构的实例。您可使用 InitOnceInitialize 函数来执行初始化。对同步一次性初始化和异步一次性初始化而言,这是必须完成的,并且必须在任何其他一次性初始化函数使用结构之前完成。其定义如下:

  显示了上面使用的同一个 GetLogger 函数,但现在它被重新编写,以使用异步一次性初始化。我们将逐步解释本例程。线程做的第一件事是调用 InitOnceBeginInitialize。该线程必须提供指向待使用的 INIT_ONCE 结构的指针,并且 dwFlags 参数应设置为 INIT_ONCE_ASYNC,以指定此线程正尝试开始异步一次性初始化。通过使用其余两个函数,fPending 和 Context,将初始化状态传回给调用方,上述两个参数均被指定为指针以便函数可以更新它们。如果成功,InitOnceBeginInitialize 返回 TRUE,否则返回 FALSE,表示有些地方已经出错,初始化无法继续。必须检查 fPending 以确认其他线程是否已完成初始化。如果 fPending 是 FALSE,则初始化已经完成,并且已将初始化对象存入 Context 参数。在此例中,GetLogger 剩下的唯一工作就是将 Context 转换为 Logger* 并将它返回给调用方。

  在本文章中,我们向您介绍了 Windows Vista 中许多适于本机 C/C++ 开发人员的线程同步原语的重要增强功能。这些增强功能可让开发人员更容易地解决线程同步问题。但本文只涉及了一些皮毛。我们鼓励您更深入地研究它们以及其他更改。对于初学者,请先试着了解线程池 API 增强功能。很遗憾,我们没有机会在此讨论线程池 API 增强功能了,但它们确实是值得思考的宝贵的新增功能。

  为什么要进行线程同步?在程序中使用多线程时,一般很少有多个线程能在其生命期内进行完全独立的操作。更多的情况是一些线程进行某些处理操作,而其他的线程必须对其处理结果进行了解。正常情况下对这种处理结果...

  多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系...

  多线进程和线程是操作系统里面经常遇到的两个概念,还有一个概念,是应用程序。应用程序包括指令和数据,在开始运行之前,只是分布在磁盘上的指令和数据。正在执行的应用程序称为进程,进程不仅仅是指令和数据,它还有状...博文

  本文首先介绍了Java的线程基本操作(创建、等待等),线程的互斥、同步操作;然后解释了为什么需要互斥、同步,什么是信号(通知)丢失,什么是虚假唤醒;并实验展示了原子性(Atomic)问题、通知丢失(n...博文来自:xusiwei1236的技术博客

  本文解析了synchronized的底层实现,介绍了JVM中优化锁的一些方法,以及实际编程中使用的一些锁优化方法。...博文来自:

  (转)彻底解决工行U盾windows7驱动程序无法使用的问题vista下U盾驱动问题也可以参考此方法...

  原语是指几个(2个及以上)连续执行,同时不能被打断的操作。打断这些连续的操作,有两种可能。一种是主动的,一种是被动的。主动的打断是指程序主动进入睡眠,而出现的操作中断。被动是指在操作期间发生外部中断,...

  当你插入移动硬盘、鼠标、SD卡、MP3等驱动时,系统提示要安装驱动,这个SB系统说它找到通用卷的驱动了,就是安装的时候出了点问题,它找不到指定的文件了可笑的是微软出了一个号称可以解决这个问题的f...

  04-05阅读数 770目前的WindowsVista已经可以从现有老版本的系统上升级,不过这要求系统盘具有至少15GB的剩余空间,并且要求两个系统地语种必须一致。除了升级安装,我们还可以进行全新安装,在进行全新安装的时候也...

  虽然TabletPC版的XP中也有墨球这个游戏,不过运行起来在游戏窗口范围内没有鼠标光标,不太爽.昨天无聊的时候又在尝试VAIO可以破解些什么,偶然发现这个游戏可以成功破解,没有任何问题,而且有鼠标光...

  阅读数 57事先准备:装好xp系统的电脑一台,vista安装iso。    注:(1)WindowsVista要求安装在NTFS分区,但是其他分区可以是FAT32格式!    (2)简单无损NTFS转换方式:运行...博文

  Volatile简介volatile的作用在多线程并发编程中synchronized和volatile都扮演着重要的角色,volatile是轻量级的synchronized,它在多处理器开发中保证了共...

  微软讨论社区hive上出现了有趣的讨论题:购买到漂亮的Vista盒装机油桶包装后,有很多人会问该如何开包,你是怎么打开Windows Vista包装的?是小心翼翼地开,尽量保留...

  这是我c+的课程设计。内容:完整c++代码(含测试代码),exe文件,完美的课程设计报告(优!)。 代码运行平台:visual studio 2005 ,窗体应用程序(windows application).br游戏仿照...

  世界之窗浏览器是由凤凰工作室出品的一款多标签、多窗口浏览器。它完全免费,没有任何功能限制,不捆绑任何第三方软件,可以干净卸载,请您放心使用。 世界之窗浏览器可运行于 Windows 2000/XP/2003/Vista/7 系列操作系统...

  引言:在很多用户在犹豫“我是否要安装Vista”时,有的网友采取了一个“折衷”的方案:模仿。通过一些第三方软件、主题包等,网友将WindowsXP模仿成WindowsVista界面的样子,在效果方...

  Windows Vista SP2 MSDN版本种子,是从MSDN官方网站上下载回来做种的,每天晚上8点到12点做种

  教务工具箱包括成绩统计、成绩分段、多排表、表格比较与填充、检测同列重复值、考场编排助手、转换座位表、批量导入图片、批量读取文件名、文件批量改名、表格数据合成、批量设置上下标、多表合并等在学生数据分析、处理中常用的多个工具,运行平台为Mis...

  微软精心打造的Vista系统,为什么死得这么快?02-24阅读数 959

  (点击上方公众号,可快速关注)转自:36氪(编译组出品。编辑:郝鹏程、王雅琪)编者按:从后来的很多反馈看来,Vista都是一个超前于时代的操...

  万能网卡驱动,win7/winvista/winxp/win2000/winme/win98se

  卷积神经网络是深度学习的基础,但是学习CNN却不是那么简单,虽然网络上关于CNN的相关代码很多,比较经典的是tiny_cnn(C++)、DeepLearnToolbox(Matlab)等等,但通过C语...

  【深度剖析HMM(附Python代码)】1.前言及隐马尔科夫链HMM的背景04-27阅读数 1万+

  小憩之后,继续为你解读AndFix热修复框架,呵呵。上一篇Alibaba-AndFix Bug热修复框架的使用已经介绍了AndFix的使用,这篇主要介绍AndFix原理以及源码解析。AndFix原理A...

  做MFC+opencv项目时,对于我来说,将视频显示到相应控件上(static或者picture)这个问题一直存在,虽然之前写个一个帖子,介绍了一种将opencv的显示window贴到相应控件上的方法...

  0.绪论这篇文章主要为了研究双目立体视觉的最终目标——三维重建,系统的介绍了三维重建的整体步骤。双目立体视觉的整体流程包括:图像获取,摄像机标定,特征提取(稠密匹配中这一步可以省略),立体匹配,三维重...

  帐号相关流程注册范围 企业 政府 媒体 其他组织换句话讲就是不让个人开发者注册。 :)填写企业信息不能使用和之前的公众号账户相同的邮箱,也就是说小程序是和微信公众号一个层级的。填写公司机构信息,对公账...

  webService学习(二)—— 调用自定义对象参数 本文主要内容: 1、如何通过idea进行webService Client的简单实现(不再使用wsimport的方式,其实是ide帮我们做了...

  灰度图像的自动阈值分割(Otsu 法)机器视觉领域许多算法都要求先对图像进行二值化。这种二值化操作阈值的选取非常重要。阈值选取的不合适,可能得到的结果就毫无用处。今天就来讲讲一种自动计算阈值的方法。这...

  最近比较有空,大四出来实习几个月了,作为实习狗的我,被叫去研究Docker了,汗汗! Docker的三大核心概念:镜像、容器、仓库 镜像:类似虚拟机的镜像、用俗话说就是安装文件。 容器:类似一个轻量...

  jquery/js实现一个网页同时调用多个倒计时(最新的) 最近需要网页添加多个倒计时. 查阅网络,基本上都是千遍一律的不好用. 自己按需写了个.希望对大家有用. 有用请赞一个哦! //js ...

  局部异常因子算法-Local Outlier Factor(LOF)在数据挖掘方面,经常需要在做特征工程和模型训练之前对数据进行清洗,剔除无效数据和异常数据。异常检测也是数据挖掘的一个方向,用于反...

  强连通分量: 简言之 就是找环(每条边只走一次,两两可达) 孤立的一个点也是一个连通分量   使用tarjan算法 在嵌套的多个环中优先得到最大环( 最小环就是每个孤立点)   定义: int Ti...

  1. 基本概念     方向导数:是一个数;反映的是f(x,y)在P0点沿方向v的变化率。     偏导数:是多个数(每元有一个);是指多元函数沿坐标轴方向的方向导数,因此二元函数就有两个偏导数。  ...

  结果分析(): 1.聚合报告 Aggregate Report 是 JMeter 常用的一个 Listener,中文被翻译为“聚合报告”。今天再次有同行问到这个报告中的各项数据表示什么意思,...

  上一篇文章讲解了SNMP的基本架构,本篇文章将重点分析SNMP报文,并对不同版本(SNMPv1、v2c、v3)进行区别! 四、SNMP协议数据单元 在SNMP管理中,管理站(NMS)和代理(Age...

  oqqsoap1234567:这只能让一行高亮,sendmessage一次,之前的就消失了,怎么让多行高亮?

  oqqsoap1234567:这只能让一行高亮,sendmessage一次,之前的就消失了,怎么让多行高亮?

  u013453581:好厉害!像楼主学习,本人现在也在准备微策略的笔试。

本文链接:http://chuyenchame.com/duruyuanyu/52.html