【问题标题】:Thread Yield method simulation using Sleep使用 Sleep 的 Thread Yield 方法模拟
【发布时间】:2012-09-14 17:23:24
【问题描述】:

我正在尝试理解 .Net 中的线程概念。 我无法使用 Yield() 方法。当我被 10 整除时,我希望控件转到并行线程。

请帮忙。

下面是我的示例代码:

class ThreadTest
{
    //Index i is declared as static so that both the threads have only one copy
    static int i;


    static void Main(string[] args)
    {
        Thread t = new Thread(WriteY);          
        i = 0;

        //Start thread Y    
        t.Start();                              
        //Do something on the main thread.
        for (; i < 100; i++)
        {
            if (i % 10 == 0)
            {
                //Simulate Yield() function
                Thread.Sleep(0);
                Console.WriteLine("The X thread");
            }
            Console.Write(i + ":X ");
        }
        Console.ReadKey(true);
    }

    static void WriteY()
    {
        for (; i < 100; i++)
        {
            if (i % 10 == 0)
            {
                //Simulate Yield() function
                Thread.Sleep(0);
                Console.WriteLine("The Y thread");
            }
            Console.Write(i + ":Y ");
        }
    }
}

我得到编译时错误:

System.Threading.Thread 不包含“Yield”的定义

Tudor 回答。此方法仅适用于 .Net 4.0 及更高版本。

理想情况下,我希望一个线程启动并希望每个线程执行 10 个 i 递增。使用我目前的方法,我要么得到所有的“X”,要么得到所有的“Y”。

编辑: 使用 Tudor 和 TheHe 的输入 - 我已经能够获得交替的 X 和 Y。问题的症结在于锁定对象的使用。但是这段代码的输出是不可预测的。

【问题讨论】:

  • 您说“行不通”——好吧,您期望会发生什么,您如何检查实际会发生什么?线程是各种复杂的,Thread.Yield 是一个令人难以置信微妙的东西,可以尝试观察。我已经完成了大量线程工作,而这不是一种我曾经需要使用的方法。退后一步:你能解释一下你想在这里做什么吗?
  • Yield 是一种 .NET 4.0+ 方法。你在运行那个版本吗?
  • @Tudor,不。我正在使用 VS 2008 和 .Net 3.5。感谢您的澄清。
  • 通过您的编辑,@Tudor 是完全正确的——您只是针对错误的框架版本。更新到 4.0 或 4.5;然而!我不认为这种方法会做认为它会做的事情。
  • 在低于 4.0 的 .NET 版本中,您可以根据需要在一定程度上模拟 YieldThread.Sleep(0),但无论如何,上面的代码不会真正起作用。

标签: c# .net multithreading


【解决方案1】:

Thread.Yield 只会让调度程序选择一个准备好运行的不同线程:

使调用线程让步给另一个线程执行 准备在当前处理器上运行。操作系统选择 要屈服的线程。

如果您的应用程序中的其他线程也在等待该锁,您可以让出所有您想要的,它们将没有机会运行。

顺便说一句,Yield 是一种 .NET 4.0+ 方法。确保您没有针对早期版本。

编辑:IMO,要做你想做的事,你应该使用事件:

class Test
{
    //Index i is declared as static so that both the threads have only one copy
    static int i;

    static AutoResetEvent parentEvent = new AutoResetEvent(true);
    static AutoResetEvent childEvent = new AutoResetEvent(false);

    static void Main(string[] args)
    {
        Thread t = new Thread(WriteY);

        i = 0;

        //Start thread Y
        t.Start();
        // Print X on the main thread
        parentEvent.WaitOne();
        while (i < 100)
        {                
            if (i % 10 == 0)
            {
                childEvent.Set();
                parentEvent.WaitOne();
            }
            Console.Write(i + ":Y ");
            i++;
        }
        t.Join();
    }

    static void WriteY()
    {
        childEvent.WaitOne();
        while (i < 100)
        {
            if (i % 10 == 0)
            {
                parentEvent.Set();
                childEvent.WaitOne();
            }
            Console.Write(i + ":X ");
            i++;
        }
    }
}

【讨论】:

  • 谢谢,这回答了我的问题。我已经更新了我的代码块。你能告诉我我使用 Sleep 模拟 Yield 有什么问题吗?
  • 这段代码非常棒!它为我执行的 5 次运行生成了相同的输出。现在让我深入研究这段代码!非常感谢!
  • 重新实现AutoResetEvent;您可以使用Monitor 更简单地做到这一点。
  • @Marc Gravell:事实上你是对的,但我一直对事件有弱点。 :D
  • @Tudor 值得克服的是 - 事件 来自操作系统、IIRC,并且涉及更多开销。 Monitor 完全在 CLR 内部处理,因此不涉及通过互操作访问操作系统。
【解决方案2】:

忘记Thread.Yield;这与您尝试做的事情无关。最终,您有一个lock,它使用Monitor 来同步访问。在lock 内,您的线程独占访问权限。你需要做的是暂时放弃锁;你这样做的方式是使用Monitor.Wait。但是,如果你Wait,你最终也会进入“等待”队列而不是“就绪”队列,所以为了确保每个线程都得到关注,我们还需要Pulse,都在@之前987654328@,以及最后(以确保两个线程都有机会退出)。我们开始:

using System.Threading;
using System;
class ThreadTest
{
    //Index i is declared as static so that both the threads have only one copy
    static int i;

    //The lock object
    static readonly object locker = new object();

    static void Main(string[] args)
    {
        Thread t = new Thread(WriteY);

        i = 0;

        //Start thread Y
        t.Start();
        lock (locker)
        {
            // Print X on the main thread
            for (; i < 100; i++)
            {
                if (i % 10 == 0)
                {
                    Monitor.PulseAll(locker); // move any "waiting" threads to the "ready" queue
                    Monitor.Wait(locker); // relinquish the lock, and wait for a pulse
                    Console.WriteLine("The X thread");
                }
                Console.Write(i + ":X ");
            }
            Monitor.PulseAll(locker);
        }
        Console.ReadKey(true);
    }

    static void WriteY()
    {
        lock (locker)
        {
            for (; i < 100; i++)
            {
                if (i % 10 == 0)
                {
                    Monitor.PulseAll(locker); // move any "waiting" threads to the "ready" queue
                    Monitor.Wait(locker); // relinquish the lock, and wait for a pulse
                    Console.WriteLine("The Y thread");
                }
                Console.Write(i + ":Y ");
            }
            Monitor.PulseAll(locker); // move any "waiting" threads to the "ready" queue
        }
    }
}

【讨论】:

  • 这个解决方案很好用!它提供了 X 和 Y 之间的完美交替。但它也打印最后一行:100X。但这不应该发生。你认为为什么会这样?
  • 我明白了。将 Console.Write(i + ":X "); 移动到 for 循环的开头。
【解决方案3】:

从我的角度来看,您正在锁定当前线程中的“储物柜”,并希望将当前任务交给其他线程... 锁一直由第一个线程持有——它不能工作?!

如果你想使用多个线程,你必须手动锁定对象...

【讨论】:

  • 我已经更新了这个问题。不管有没有锁,我都会收到编译时错误。
  • 所以基本上,如果有锁的话,yield 不会有任何效果。是这个意思吗?
  • 更糟糕的是:yield想改变线程,但是锁同时持有在另一个线程上......
猜你喜欢
  • 2019-09-27
  • 1970-01-01
  • 2019-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多