0

I've been having trouble getting the below C# threading code to behave properly. I start a thread by clicking a button on a form, and the thread simply declares a float x and increments it in a loop until threadRunning is set to false. I set threadRunning to false when I click the button again. When the loop completes, the thread sets threadResult to the incremented value x. I then display threadResult on the form.

The problem is that threadResult always gets set to 256.0 in this case. If I choose to increment x by 0.0001f, then threadResult will always get set to 2048.0. These fixed values are assigned to threadResult every time the thread runs.. unless I put a breakpoint on the incremented line, x += 0.00001f;. This can be a conditional breakpoint with the condition set to false; as long as an enabled breakpoint exists on this line, x is incremented properly and gets assigned to threadResult.

---Edit 1--- To clarify what proper behavior is, which occurs only when an enabled breakpoint exists on the incremented line: threadResult gets assigned a value equal to the number of times the incrementing line is hit multiplied by the increment value. So, if x was incremented 121 times, and the increment value is 0.00001f, threadResult should become 0.00121. This does occur when an enabled breakpoint exists on the incremented line and I'm running in debug mode.

Also, when I run this in release mode, the program hangs when I click the button to stop the thread, so I'm assuming something is wrong with my threading implementation entirely. I would still appreciate any pointers on what might be the problem with my code. I just wanted to write something to test stopping a thread. Also, I don't want to use BackgroundWorkers, because my threading code isn't intended to be used with Windows Forms. ---End Edit 1---

Can anyone explain to me what is going on here? Is the method that I use to stop the thread okay, by setting the thread's loop control variable to false outside of the thread? I've tried this code in Visual Studio 2017, 2015, and 2013, all with the same result.

Here is the code. The form contains only a button named button1 and a textbox named textBox1.

using System;
using System.Windows.Forms;
using System.Threading;

namespace ThreadingTesting
{
   public partial class Form1 : Form
   {
      public Form1()
      {
         InitializeComponent();
         button1.Click += Button1_Click;
      }

      private Thread thread;
      private bool threadRunning = false;
      private float threadResult = 0;

      private void Button1_Click(object sender, EventArgs e)
      {
         if (threadRunning)
         {
            threadRunning = false;
            thread.Join();
            textBox1.Text = "Result: " + threadResult.ToString("N4");
            button1.Text = "Start Thread";
         }
         else
         {
            thread = new Thread(new ThreadStart(ThreadedWork));
            thread.Start();
            textBox1.Text = "Thread Started!";
            button1.Text = "Stop Thread";
         }
      }

      private void ThreadedWork()
      {
         threadRunning = true;
         float x = 0.0f;
         while (threadRunning)
         {
            x += 0.00001f;
         }
         threadResult = x;
         return;
      }
   }
}
mjwills
  • 23,389
  • 6
  • 40
  • 63
bgg
  • 3
  • 3
  • Try looking up the keyword `volatile`. – Enigmativity Aug 30 '17 at 12:46
  • 3
    `thread only runs properly when breakpoint exists` What is the improper behaviour you are seeing? What is the proper behaviour you are seeking? – mjwills Aug 30 '17 at 12:46
  • If your trying to not block the UI you should look into `async`/`await` – Liam Aug 30 '17 at 12:48
  • I think you will find that 256 is the value where adding 0.00001f has any affect at floating point accuracy. – PaulF Aug 30 '17 at 12:56
  • 2
    Possible duplicate of [Adding floats does not yield a correct result](https://stackoverflow.com/questions/17373909/adding-floats-does-not-yield-a-correct-result) – mjwills Aug 30 '17 at 13:00
  • 3
    Neither the threading code nor the math code is correct here. Threading logic and floating point arithmetic are both tricky and require considerable knowledge to get correct. Do not make the variable volatile; **learn why it is that if you have to make a variable volatile to fix a threading problem, you are probably solving the problem with the wrong tool**. – Eric Lippert Aug 30 '17 at 13:02

2 Answers2

3

256 + 0.00001f unfortunately results in 256. This is why it never proceeds past 256.

Consider using double (d) rather than float (f).

mjwills
  • 23,389
  • 6
  • 40
  • 63
  • Or better - `decimal` (M). `256M + 0.00001M`. – i486 Aug 30 '17 at 13:51
  • Okay, using `double` works. I must not have changed all doubles when I made my first edit. This still doesn't explain why it works with `float`s only when a breakpoint exists. – bgg Aug 31 '17 at 12:12
  • @bgg That is because you have two threads reading and writing to the same non-`volatile` variable without a memory barrier (https://stackoverflow.com/questions/3493931/why-do-i-need-a-memory-barrier). Would you consider using `BackgroundWorker` instead? – mjwills Aug 31 '17 at 12:14
-1

the answer is here

Why Thread.Join() DOES NOT hang my application when called on UI thread?

the method Button1_Click is called in UI Thread and so the other thread will not run properly.

I think than you have to approach Task or BackgroundWorker...