-1

I think the title speaks for itself. Simply I have some threads that run with a random order instead of the order I planned. This is a sample code:

    event strHandler strChanged;
    delegate void strHandler(string str);

    public Form1()
    {
        InitializeComponent();

        strChanged += new strHandler(updatestr);
    }

    public void updatestr(string str)
    {
        Thread th = new Thread(new ParameterizedThreadStart(updatethr));
        th.IsBackground = true;
        th.Start(str); 
    }

    object obj = new object();

    private void updatethr(object str)
    {
        lock (obj)
        {
            SystemUtilities.SetControlPropertyThreadSafe(textBox1, "Text", (string)str);
            Thread.Sleep(1000);
        }
    }

    private void button1_Click(object sender, EventArgs e)
    {
        this.Text = write();
    }

    private string write()
    {
        string res = "";
        strChanged(res);
        for (int i = 0; i <= 5; i++)
        {
            res += i.ToString();
            strChanged(res);
        }
        return res;
    }

Note: SystemUtilities.SetControlPropertyThreadSafe(textBox1, "Text", (string)str) is a function (used to avoid cross thread exception) that set textBox1.Text to str. When you press button1 this.Text will be set instantly to the result of write() function ("012345"). The string returned is res that is build inside write() starting from an empty string and, iteratively, appending numbers from 0 to 5. When the string is created and for each number added to res, the event strChanged is raised calling updatestr method. Every time that updatestr is called a thread is created and it starts calling updatethr. Here textBox1.Text is set to str (that should be progressively "", "0" , "01", "012", "0123", "01234", "012345") and wait a second before exiting the method. Using lock statement the threads created in updatestr should wait the end of the previous threads before modifying textBox1.Text. Running this code I obtain sequences of values for textBox1.Text that don't match the expected sequence as if the threads don't start in order with their creation in updatestr. Why does this happen? How can I fix that? Thanks in advance!

EDIT: If want to try this code you can replace SystemUtilities.SetControlPropertyThreadSafe(textBox1, "Text", (string)str) with System.Windows.Forms.MessageBox.Show(str)

PrinceOfBorgo
  • 508
  • 5
  • 14
  • 1
    You are assuming that because the threads were started in a certain order they run in the same order and that order is the order in which the lock is acquired. Neither of those assumptions need be true. There is no guarantee that by the OS that the threads will be scheduled in a particular order. Locks have no guarantee of fairness in C#. – Mike Zboray Jan 02 '15 at 03:47
  • 1
    I'm pretty certain that the threads are starting in the order that you initiate them...but after that they are in a race. Whoever reaches the finish line first, is going to be the first to lock and write. This can be the second "racehorse", or the third "racehorse", and you'll get a funny result at the end like "201345". If you really need them to arrive in a certain order, then you should have this code running on a single thread. The other alternative is to start each thread after the previous one has ended or about to end. – MingShun Jan 02 '15 at 03:47
  • But how can I solve this problem? – PrinceOfBorgo Jan 02 '15 at 03:49
  • Why does it have to be multithreaded in the first place? That would ultimately affect the design. However, if you really must do something like this, you can probably emulate the TCP protocol. Designed with the randomness of the internet in mind, basically each packet that is sent out is assigned an order number. When it appears at the end, that order number will be used to manually reassemble the final message in the correct order. – MingShun Jan 02 '15 at 03:51
  • This is just an example of a problem I have in another application that is a shortest path finder (similar to this http://qiao.github.io/PathFinding.js/visual/). I have a function that finds the shortest path and returns it as a list of points. For each node of the map that is examined I want to update the preview of the map to see what happens step by step (as you can see in the link). To do this I need to intercept every alteration of the nodes, update the map and wait some milliseconds before the next update. – PrinceOfBorgo Jan 02 '15 at 04:01
  • @mikez: "Locks have no guarantee of fairness in C#" -- this is incorrect (though the main point of your comment is useful). Unlike native Win32 synchronization objects, the monitor (used by `lock` and the `Monitor` class) issues the lock to threads round-robin, based on a FIFO queue. Order in this scenario is not guaranteed, but only because the initial thread execution order is not guaranteed; after that, .NET does ensure that if N threads are all currently waiting for a lock, then each will acquire the lock before any gets to acquire it a second time (i.e. the lock is "fair"). – Peter Duniho Jan 02 '15 at 07:04
  • This question as asked isn't useful. It is a well-understood fact that thread execution order is not guaranteed on Windows, or in .NET, and the code example you posted is clearly contrived so there's no useful way to show an implementation that does what you want. That is, it's trivial to write a code example that has the observable result you say you'd like to see, but there's nothing in your question to suggest that would actually address the real-world situation you have. You should either fix this question or post a new one that is actually answerable in a useful way. – Peter Duniho Jan 02 '15 at 07:10
  • @PeterDuniho That is an interesting detail, but whether Microsoft's current implementation uses a FIFO queue does not change the fact that no guarantee is made with regard to lock fairness. See Eric Lippert's [answer](http://stackoverflow.com/a/16678958/517852) and [this question](http://stackoverflow.com/questions/4228864/does-lock-guarantee-acquired-in-order-requested). – Mike Zboray Jan 02 '15 at 07:25
  • @mikez: thanks...I must be confusing the monitor with some other .NET synchronization object. The behavior I'm describing is _documented_ and is not simply an implementation detail. But I don't see the documentation I'm thinking of in the `Monitor` class, which suggests I've misremembered which synchronization object has this behavior. Sorry for the confusion...now I'm wondering which synchronization object it is that _does_ have a FIFO guarantee ('cause I know it's not `Semaphore` either). – Peter Duniho Jan 02 '15 at 07:42

1 Answers1

1

I believe you are looking for a different threading strategy. It appears you need a single thread, to maintain order, that's different from the Form's main thread to finish an operation. By using a BlockingCollection, you can sequentially have a different thread operate on the string.

I would rewrite the code this way:

event strHandler strChanged;
delegate void strHandler(string str);

public Form1()
{
    InitializeComponent();

    Thread th = new Thread(new ThreadStart(updatethr));
    th.IsBackground = true;
    th.Start();

    strChanged += new strHandler(updatestr);
}

BlockingCollection<string> bc = new BlockingCollection<string>();

public void updatestr(string str)
{
    bc.Add(str);
}

private void updatethr()
{
    while(true)
    {
        string str = bc.Take();
        SystemUtilities.SetControlPropertyThreadSafe(textBox1, "Text", (string)str);
        // Not sure why you need this here, other than simulating a long operation.
        // Thread.Sleep(1000); 
    }
}

private void button1_Click(object sender, EventArgs e)
{
    this.Text = write();
}

private string write()
{
    string res = "";
    strChanged(res);
    for (int i = 0; i <= 5; i++)
    {
        res += i.ToString();
        strChanged(res);
    }
    return res;
}
Frank Liao
  • 641
  • 1
  • 6
  • 9