在窗口上的虚假唤醒。是否可以? [英] Spurious wakeups on windows. Is it possible?

查看:218
本文介绍了在窗口上的虚假唤醒。是否可以?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近学到了虚假的唤醒
任何人都说这个问题可能只对一些类型的Linux电脑。



我使用windows。



我写了测试虚假的唤醒。我得到的结果,这是可能的。但我想为你展示这个测试。



我的初始变体:

  import java.util.Random; 
import java.util.concurrent。*;
import java.util.concurrent.atomic.AtomicInteger;

public class TestSpuriousWakeups {
static final int MAX_THREADS = 600;

static final Object mutex = new Object();

static final CountDownLatch allThreadsStarted =
new CountDownLatch(MAX_THREADS);
static final CountDownLatch allThreadsFinished =
new CountDownLatch(1);

static / * final * / volatile AtomicInteger processedThreads = new AtomicInteger();
static / * final * / volatile AtomicInteger notificationsThreads = new AtomicInteger();

final int n = 10;

static volatile boolean continueCondition = true;

static final随机sleepRandom = new Random();

static class Worker extends Thread {
public void run(){
try {
synchronized(mutex){
allThreadsStarted.countDown();

mutex.wait();
}

continueCondition = true;
} catch(Exception e){
throw new RuntimeException(e);
} finally {
processedThreads.incrementAndGet();
}
}
}

static class Notifier extends Thread {
public void run(){
while(true){

if(processedThreads.get()== MAX_THREADS)
break;

synchronized(mutex){
doStuff();

mutex.notify();
continueCondition = false;
notifiedThreads.incrementAndGet();
}
}

allThreadsFinished.countDown();
}

//只是为了模拟一些活动
void doStuff(){
try {Thread.sleep(sleepRandom.nextInt(5)); }
catch(InterruptedException e){
throw new RuntimeException(e);
}
}
}

public static void main(String [] args)throws Exception {
for(int i = 0; i< MAX_THREADS; i ++)
new Worker()。start();

//等待所有工人开始执行
allThreadsStarted.await();

new Notifier()。start();

//等待所有工人和通知程序完成执行
allThreadsFinished.await();

System.out.println(伪唤醒计数:
+(MAX_THREADS - informedThreads.get()));
}
}

4随机执行:

$ b $虚假的唤醒次数:-20
虚假的唤醒次数:-5
虚假的唤醒次数:0
虚假的唤醒次数:0
< -407

因此,不同的值对我来说是奇怪的。



我添加了一对行运行方法:

  static class Notifier extends Thread {
public void run (){
while(true){

while(!continueCondition)//添加字符串
doStuff //添加字符串

//所有线程都完成执行
if(processedThreads.get()== MAX_THREADS)
break;

synchronized(mutex){
doStuff();

mutex.notify();
continueCondition = false;
notifiedThreads.incrementAndGet();
}
}

allThreadsFinished.countDown();
}

之后我无法得到

 虚假的唤醒计数:0 



PS



我注意到我看到了负面的数字。因此显然是实验bug。

    解决方案


  1. 虚假的唤醒是真实的,即使在Windows上。这在WinAPI中有所记录: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682052(v=vs.85).aspx

  2. 您有一个竞争条件在你的测试。


  3. 比赛是在工作者线程中的同步块的退出之间,而当他们到达processedThreads.incrementAndGet()。通知程序将在该时间旋转,通知线程可能或可能没有获取锁。



    换句话说,


    1. Notifier可以旋转两次

    2. 在最后一个线程退出同步块但尚未到达finally块之后,Notifier可能会旋转。
    3. / li>

    您添加的两行会更改输出,因为通过减慢通知程序,您将掩盖种族。 (通过给予工作者很多时间来输入互斥体。)



    希望这有一些意义。


    I recently learned "Spurious wakeups" Any people say that this problem possible only for some types of Linux PC.

    I use windows.

    I wrote test for Spurious wakeups. I got result that it is possible. But I want to show this test for you. Maybe I made mistake somewhere.

    my initial variant:

    import java.util.Random;
    import java.util.concurrent.*;
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class TestSpuriousWakeups {
        static final int MAX_THREADS = 600;
    
        static final Object mutex = new Object();
    
        static final CountDownLatch allThreadsStarted =
                new CountDownLatch(MAX_THREADS);
        static final CountDownLatch allThreadsFinished =
                new CountDownLatch(1);
    
        static /*final*/ volatile AtomicInteger processedThreads = new AtomicInteger();
        static /*final*/ volatile AtomicInteger notifiedThreads = new AtomicInteger();
    
        final  int n  = 10;
    
        static volatile boolean continueCondition = true;
    
        static final Random sleepRandom = new Random();
    
        static class Worker extends Thread {
            public void run() {
                try {
                    synchronized (mutex) {
                        allThreadsStarted.countDown();
    
                        mutex.wait();
                    }
    
                    continueCondition = true;
                } catch (Exception e) {
                    throw new RuntimeException(e);
                } finally {
                    processedThreads.incrementAndGet();
                }
            }
        }
    
        static class Notifier extends Thread {
            public void run() {
                while (true) {
    
                    if (processedThreads.get() == MAX_THREADS)
                        break;
    
                    synchronized (mutex) {
                        doStuff();
    
                        mutex.notify();
                        continueCondition = false;
                        notifiedThreads.incrementAndGet();
                    }
                }
    
                allThreadsFinished.countDown();
            }
    
            // just to emulate some activity
            void doStuff() {
                try { Thread.sleep(sleepRandom.nextInt(5)); }
                catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    
        public static void main(String[] args) throws Exception {
            for (int i = 0; i < MAX_THREADS; i++)
                new Worker().start();
    
            // wait for all workers to start execution
            allThreadsStarted.await();
    
            new Notifier().start();
    
            // wait for all workers and notifier to finish execution
            allThreadsFinished.await();
    
            System.out.println("Spurious wakeups count: "
                    + (MAX_THREADS - notifiedThreads.get()));
        }
    }
    

    4 random execution:

    Spurious wakeups count: -20
    Spurious wakeups count: -5
    Spurious wakeups count: 0
    Spurious wakeups count: -407
    

    So different values is wondering for me.

    I added pair of rows to run method:

    static class Notifier extends Thread {
            public void run() {
                while (true) {
    
                    while (!continueCondition)  //added string
                        doStuff();              //added string            
    
                    // all threads finished their execution
                    if (processedThreads.get() == MAX_THREADS)
                        break;
    
                    synchronized (mutex) {
                        doStuff();
    
                        mutex.notify();
                        continueCondition = false;
                        notifiedThreads.incrementAndGet();
                    }
                }
    
                allThreadsFinished.countDown();
            }
    

    after it I cannot get something another than

    Spurious wakeups count: 0
    

    Is it really Spurious wakeups or bug in my experiment ?

    P.S.

    I noticed that I see negatives numbers. Thus obviously it is experiment bug. But I don't understand cause.

    解决方案

    Two things

    1. Spurious wake ups are real, even on Windows. This is documented in the WinAPI: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682052(v=vs.85).aspx
    2. You have a race condition in your test. So, I don't think it's quite accurate.

    The race is between the exit of the synchronized block in your worker threads and when they reach processedThreads.incrementAndGet(). Notifier will spin during that time, notifying threads which may or may not have acquired the lock.

    In other words

    1. It's possible for Notifier to spin twice (that is, notify() twice) before a worker thread can acquire the mutex.
    2. It's possible for Notifier to spin after the last thread has exited the synchronized block but not yet reached its finally block.

    Your two added lines change the output because, by slowing down the Notifier, you're masking the race. (By giving Worker lots of time to enter the mutex.)

    Hope that makes some sense.

    这篇关于在窗口上的虚假唤醒。是否可以?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆