简体   繁体   English

为什么价值变量没有改变?

[英]why value variable is not changed?

I have a Timer in my winForms and when the TimeSpan is 0 I set done as true and try check the value a thread. 我的winForms中有一个Timer ,当TimeSpan0我将done设置为true然后尝试检查线程的值。

My code: 我的代码:

Initial variables: 初始变量:

private DateTime endTime = DateTime.UtcNow.AddMinutes(1);
private bool done;
private bool running = true;
private int count = 0;
private delegate void setLabelMethod(string msg);
private void setLabelValue(string msg) { label2.Text = msg; }

Timer handling: 定时器处理:

private void timer1_Tick_1(object sender, EventArgs e)
        {
            TimeSpan remainingTime = endTime - DateTime.UtcNow;

            if (remainingTime <= TimeSpan.Zero)
            {
                label1.Text = "Done!";
                timer1.Enabled = false;
                done = true;
                running = false;
            }
            else
            {
               //...
            }

        }

Thread callback function: 线程回调函数:

private void test()
        {
            do
            {
                if (done)
                {
                    Invoke(new setLabelMethod(setLabelValue), "yeah");
                    done = false;
                }

                Thread.Sleep(500); 

            } while (running);
        }

Start timer1_Tick and Thread executions. 启动timer1_TickThread执行。

 private void button2_Click(object sender, EventArgs e)
        {
            Thread th = new Thread(new ThreadStart(test));
            th.Start();
            timer1.Enabled = true;
        }

the problem is that the following statement in .test() method not ever is true ,why? 问题是.test()方法中的以下语句永远都不true ,为什么?

statement 声明

 if (done)
 {
    Invoke(new setLabelMethod(setLabelValue), "yeah");
    done = false;
 }

Someone can point my mistake? 有人可以指出我的错误吗? Thanks in advance. 提前致谢。

You have no synchronization protecting the 'done' variable. 您没有保护'done'变量的同步 When an object or variable may be accessed in one thread while another thread is, or might be, modifying it, some form of synchronization is needed. 当一个对象或变量可以在一个线程中访问而另一个线程正在或可能正在修改它时,需要某种形式的同步。

The ways it can actually fail are many. 它实际上可能失败的方式很多。 For example, consider this loop: 例如,考虑这个循环:

private void test()
    {
        do
        {
            if (done)
            {
                Invoke(new setLabelMethod(setLabelValue), "yeah");
                done = false;
            }

            Thread.Sleep(500); 

        } while (running);
    }

Suppose the compiler knows that Thread.Sleep does not modify done or running . 假设编译器知道Thread.Sleep不会修改donerunning It may conclude that nothing in this loop modifies done or running (assuming done is false when the loop is entered) and therefore it can cache done (and running !) in registers across calls to Thread.Sleep . 它可以得出结论,在这个循环中没有任何东西修改donerunning (假设done进入循环时done是假的),因此它可以在调用Thread.Sleep缓存done (并running !)。

In other words, it can 'optimize' your loop to: 换句话说,它可以“优化”你的循环:

private void test()
    {
       if (done)
           Invoke(new setLabelMethod(setLabelValue), "yeah");
       done = false;
       if (running)
          while(1) Thread.Sleep(500); 
    }

Notice that this is a bona fide optimization, if done and running don't have their values changed by other threads. 请注意,这是一个真正的优化,如果done并且running ,则其他线程不会更改其值。 The compiler is free to assume this since it violates the rules to modify a value in one thread while another thread is or might be accessing it without synchronization, this thread might be accessing those variables, and this code contains no synchronization functions. 编译器可以自由地假设这一点,因为它违反了修改一个线程中的值的规则,而另一个线程正在或可能正在访问它而没有同步,该线程可能正在访问这些变量,并且此代码不包含同步函数。

Of course, your code doesn't have to worry about this if you follow the rules. 当然,如果您遵循规则,您的代码就不必为此担心。 The compiler is prohibited from making 'optimizations' that break your code and you don't have to worry about how it does it. 禁止编译器进行破坏代码的“优化”,而不必担心它是如何做到的。 But that only applies if you follow the rule. 但这只适用于遵守规则的情况。 (In practice, the compiler will not cache a value in a register across a call to a synchronization function and volatile variables will never be cached in registers.) (实际上,编译器不会在对同步函数的调用中将值缓存在寄存器中,并且易失性变量永远不会缓存在寄存器中。)

As Martin James mentioned, instead of polling flags , you should be using thread synchronization mechanisms. 正如Martin James所提到的,您应该使用线程同步机制而不是轮询标志

This simple example shows how you can do this using Monitor.Wait , Monitor.Pulse and lock . 这个简单的例子展示了如何使用Monitor.WaitMonitor.Pulselock来实现这一点。

The important difference between this and your code is that this will stop the thread from running until the condition has been met, thus improving the performance of your code. 这与您的代码之间的重要区别在于,这将阻止线程运行,直到满足条件,从而提高代码的性能

class Program
{
    static void Main(string[] args)
    {
        ThreadExample example = new ThreadExample();
        Thread thread = new Thread(example.Run);

        Console.WriteLine("Main: Starting thread...");
        thread.Start();

        Console.WriteLine("Press a key to send a pulse");
        Console.ReadKey();

        lock (example) //locks the object we are using for synchronization
        {
            Console.WriteLine("Sending pulse...");
            Monitor.Pulse(example); //Sends a pulse to the thread
            Console.WriteLine("Pulse sent.");
        }
        thread.Join();

        Console.ReadKey();
    }
}

class ThreadExample
{
    public void Run()
    {
        Console.WriteLine("Thread: Thread has started");
        lock (this) //locks the object we are using for synchronization
        {
            Monitor.Wait(this); //Waits for one pulse - thread stops running until a pulse has been sent
            Console.WriteLine("Thread: Condition has been met");
        }
    }
}

To modify your code to use this mechanism, you need to maintain the reference to the object you used to start the thread (in this example, I will call it threadObject ) 要修改代码以使用此机制,您需要维护对用于启动线程的对象的引用(在本示例中,我将其threadObject

private void timer1_Tick_1(object sender, EventArgs e)
    {
        TimeSpan remainingTime = endTime - DateTime.UtcNow;

        if (remainingTime <= TimeSpan.Zero)
        {
            label1.Text = "Done!";
            timer1.Enabled = false;
            lock(threadObject){
                Monitor.Pulse(threadObject); //You signal the thread, indicating that the condition has been met
            }
        }
        else
        {
           //...
        }
    }

Then, in your test() method, you only need this: 然后,在test()方法中,您只需要:

private void test()
    {
        lock(this)
        {
            Monitor.Wait(this); //Will stop the thread until a pulse has been recieved.
            Invoke(new setLabelMethod(setLabelValue), "yeah");
        }
    }

In your timer1_Tick method, you have set running = false; 在你的timer1_Tick方法中,你设置了running = false; - and your thread is depending on this variable to loop. - 并且你的线程依赖于这个变量来循环。 As far as I can see, this is used to stop the thread once the condition has been met. 据我所见,这用于在满足条件后停止线程。

You could do this by moving this statement to your thread: 您可以通过将此语句移动到您的线程来执行此操作:

private void test()
    {
        do
        {
            if (done)
            {
                Invoke(new setLabelMethod(setLabelValue), "yeah");
                done = false;
                running = false;
            }

            Thread.Sleep(500); 

        } while (running);
    }

This way you ensure that the thread code will be run when the condition is met. 这样,您可以确保在满足条件时运行线程代码。 What may have been happening is that the thread had already past the if (done) statement when your timer1_Tick code sets the running = false; 可能发生的事情是当你的timer1_Tick代码设置running = false;时,线程已经超过了if (done)语句running = false; thus stopping the thread. 从而停止线程。 Remove the running = false; 删除running = false; statement from your timer1_Tick method. 来自timer1_Tick方法的语句。

Your thread spends 99.9999% of its time sleeping, then it tests the 'running' flag, exits if it is false and then tests the 'done' flag, then sleeps again. 你的线程花费99.9999%的时间休眠,然后它测试'running'标志,如果它是假的则退出,然后测试'done'标志,然后再次休眠。 The timer handler sets the 'done' flag and then clears the 'running' flag. 计时器处理程序设置“完成”标志,然后清除“运行”标志。 The chances are overwhelming that, when the timer eventually fires, the thread will find the 'running' flag cleared and so exit before it ever gets to checking the 'done' flag. 当计时器最终触发时,线程会发现“运行”标志被清除,因此在它检查“完成”标志之前退出是很可能的。 Try putting a sleep(1000) between setting the 'done' flag and clearing the 'running' flag. 尝试在设置“ done”标志和清除“ running”标志之间放置sleep(1000)。 Then try a better signaling mechanism than polling flags! 然后尝试一个比轮询标志更好的信令机制!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM