0

I have no idea what is going on in this. I'm trying to test thread safety of a class by spawning 100 threads to access it constantly, but it seems my anonymous method parameters are changing themselves to values they should never be and I'm confused as to why. As I have no idea what's going on, I'll just post all the functions involved in testing. Somehow I'm ending up with "Thread 98" getting the parameter "num = 100"... That shouldn't be possible and I have no idea what kind of hokey pokey is going on that is changing the integer. (in method "ThreadWriting(int num)" you'll see the point where I check for "num" to equal 100, where I put a break point to catch the offending thread in the act, and it breaks every time. Otherwise it throws an "IndexOutofRangeException" on the array "counts". I'm just trying to see if my threads are generally getting equal access to the class they're all trying to use at once.

    public delegate void TempDel();
    public TempDel InvokeTest;

    public void TRTest3(Form1 sender)
    {
        InvokeTest = new TempDel(UpdateInvoke);

        Thread t = new Thread(() => ConsoleUpdateTest(sender));
        t.IsBackground = true;
        t.Start();

        POConsole.Instance.MaxLines = 20;

        for(int i = 0; i < 100; i++)
        {

            Thread t2 = new Thread(() => ThreadWriting(i));
            t2.IsBackground = true;
            t2.Name = String.Format("Thread {0}", i);
            t2.Start();
        }

    }

    public ulong[] counts = new ulong[100];

    public void ThreadWriting(int num)
    {
        if(num == 100)
        {
            bool stop = true;
        }
        while (true)
        {
            POConsole.Instance.WriteLine("Hello from Thread " + num);
            counts[num]++;
        }
    }

    public void ConsoleUpdateTest(Form1 sender)
    {
        while(true)
        {
            sender.Invoke(InvokeTest);
            Thread.Sleep(5);
        }
    }



    public void UpdateInvoke()
    {
        QuickTestBox.Text = POConsole.Instance.FullFeed;
    }

All my threads are named, as you can see, and none of them receives the name "Thread 100" so I have no idea how one of the other threads could get passed a parameter of 100 or the parameter could be corrupted in some way.

Apparently my thread-safety checking isn't thread safe in some way?

ThisHandleNotInUse
  • 1,135
  • 1
  • 10
  • 23
  • I'm almost sure this is because you are using an action delegate for the thread. It will actually cache access to `i` and if `i` has changed, so will the value of `num` in the thread. Try using a `ParameterizedThreadStart` delegate instead. – Ron Beyer May 17 '15 at 02:54
  • Huh... That's interesting. I guess I misunderstood the nature of structs thinking they got copied every time they were passed around. – ThisHandleNotInUse May 17 '15 at 03:07
  • Doesn't have anything to do with structs, but everything to do with how action delegates work and (as the answer points out) closure. The action gets its value when its run, since its not run right away there's a chance it won't get the value you intend to pass into it. – Ron Beyer May 17 '15 at 03:08
  • That's great to know... I will readily admit I'm unfamiliar with precisely what is going on when I create an action delegate - but now I understand it a bit better. More precisely, I understand that the ThreadStart doesn't execute immediately upon "Start()" being called which was just kind of my previous unfounded and unconsidered assumption. I had previously not really considered the ThreadStart part of the new thread, but more like an initialization method. – ThisHandleNotInUse May 17 '15 at 03:16
  • Actually, I should have dug a bit deeper, as there's a much older, suitable duplicate answer here: [Captured variable in a loop in C#](http://stackoverflow.com/q/271440/3538012). Of course, there are many similar questions on Stack Overflow, most of which should be closed as a duplicate. – Peter Duniho May 17 '15 at 03:27
  • Generally I try to find answers and avoid duplicates - but I had no idea what precisely I was looking for in the code that was causing this error and therefore didn't know how to properly phrase a search for it. – ThisHandleNotInUse May 17 '15 at 03:34
  • Asking a duplicate question cannot be the reason to down vote it, Mr down voter – Mrinal Kamboj May 17 '15 at 03:36
  • _"didn't know how to properly phrase a search"_ -- in general, the trick is to not be so specific. Note that if you use the words "Thread start passed variable changes" as your search, the duplicate question comes up in the first page of the search results. Same thing if you use the word "value" instead of "variable". For that matter, relevant links also come up in the "Ask Question" page if you had used that more general phrase for the question title. – Peter Duniho May 17 '15 at 04:12

1 Answers1

1

This is a simple closure issue, you should not be using the for loop counter as a threading parameter issue, issue happens out here, for loop and thread execution do not run at same speed, so value of i can change for multiple threads:

for(int i = 0; i < 100; i++)
        {

            Thread t2 = new Thread(() => ThreadWriting(i));
            t2.IsBackground = true;
            t2.Name = String.Format("Thread {0}", i);
            t2.Start();
        }

Use following modification, create a local variable from loop counter

for(int i = 0; i < 100; i++)
        {
            int j = i;
            Thread t2 = new Thread(() => ThreadWriting(j));
            t2.IsBackground = true;
            t2.Name = String.Format("Thread {0}", j);
            t2.Start();
        }
Mrinal Kamboj
  • 11,300
  • 5
  • 40
  • 74
  • 1
    In fact having said that, using Thread class to create 100 threads is itself a bad idea, it will lead to lot of contention between threads, use either ThreadPool or even more preferable would be Parallel API of TPL – Mrinal Kamboj May 17 '15 at 02:57
  • Your solution worked - thank you. I've never used ThreadPool and maybe I'll take a look at it again. I've always found it easy enough and seemingly functional to just spawn my own threads with "Thread". That being said, this is just a thread safety test of the "POConsole" singleton and it's not like it's going to be implemented in anything. I don't see myself writing anything any time soon that would possibly need 100 threads. – ThisHandleNotInUse May 17 '15 at 03:11
  • Two quick things, if it worked, then please mark it as answer :). More important point would be let .Net decide the number threads required for the parallelization, it is not the programmer's job, since it can keep changing based on requirement and system. At a given time only one thread can be processed by a core, more threads than required would lead to huge contention issues and performance could even be worse than single thread. Please see the Task Parallel Library, this would make overall implementation very easy. – Mrinal Kamboj May 17 '15 at 03:19
  • Oh, yes, I was going to do that but it gives me a 10 minute cooldown so I was just kind of waiting for that 10 minutes to be up and had lost track of time. I was (and am) going to select your answer. – ThisHandleNotInUse May 17 '15 at 03:21
  • Like I said, this was just to thread safe check the singleton to ensure it didn't get into a "lock freeze" (I'm not sure what the official term is for when a bunch of threads get stuck in locks and everything grinds to a halt). I certainly don't really anticipate even three threads trying to access this class at the same time and would find two somewhat uncommon. – ThisHandleNotInUse May 17 '15 at 03:22
  • Official term for threads halting at a lock and freeze would be deadlock, my suggestion for the design of threading systems is an important policy. Using explicit Thread class is almost obsolete nowadays. .Net 4.0 onward after introducing TPL there are huge improvements while making systems parallel – Mrinal Kamboj May 17 '15 at 03:34
  • Doesn't trying to use ThreadPool somewhat undermine the whole concept of checking thread safety as ThreadPool will be reducing the massive overload of attempts at simultaneous access you want to engage in when trying to ensure everything is generally running smoothly on your class? But you are correct that I should be using it in actual applications of multithreading which I've not been doing as I've always swayed towards using "primitives" to accomplish my goals. ("Thread" was a bad habit I picked up after finding backgroundworker really annoying to work with earlier in my C# learning) – ThisHandleNotInUse May 17 '15 at 03:46
  • It would not be the case with ThreadPool, since number of threads with which you can anytime load the system and execute the program will be proportional to number of cores, rest will all be an overhead causing contention. Threadpool will be able to spawn the threads which match the requirement in most of the cases and would be efficient too. Anyway now I should say Task or Parallel.For :) – Mrinal Kamboj May 17 '15 at 04:58
  • Which is why I want to spawn multiple threads manually to test the thread safety of a class as that results in the highest chance of attempted parallel code execution. If what you say is true and ThreadPool tries to cap itself relative to the number of cores (I'm really not familiar with ThreadPool), then it's really a very bad choice for trying to "break" your thread safe class. – ThisHandleNotInUse May 17 '15 at 05:08
  • No please understand that's not correct, even if you do that it will still run 1 per core at a given time, only thing is it will take more time, as it has to provide time slice to other waiting / competing threads. Its like only 1 person is allowed per door, so best for n doors would be n people and then next n, if you allow 100n in the waiting area, then they will only push, pull and shove but not do anything worthwhile, someone will get a hand in, someone feet, but not the whole person in and would be an utter chaos for the system to handle – Mrinal Kamboj May 17 '15 at 05:13
  • I'm not clear on how the operating system handles threading. If I have more threads than cores does it take turns on the threads or does it execute an entire method on one thread then move to the next? I was under the impression that it would just kind of do one or two words from each thread then move to the next thread. I'm trying to get the threads to compete to ensure the class still only allows one at a time to modify the class. I'm not just using standard "lock" statements - there's a buffer for "pending writes" with a fallthrough if it's "busy" so only one thread is "stuck" – ThisHandleNotInUse May 17 '15 at 05:18
  • Yes they would compete for a scheduling slot but please understand it would not be good for your application, all that it would do is consume system (OS) resource and slow down execution, it will not genuinely test the application you are trying to test. That way you can even spawn 1000 threads, which will only bring the system down but not test the application as you are expecting – Mrinal Kamboj May 17 '15 at 05:39
  • To do 1000 I think I have to change the calling thread's stack size which is why it's only 100. Maybe not - I can't remember how many it took to overflow the stack last time I did this. I'm not spawning 100 threads for any application - I'm just trying to ensure a large number of parallel access attempts on this one class instance - it doesn't matter how fast the code executes as long as threads are trying it simultaneously. The only time I would try to spawn 100 threads is, maybe, if I were writing server code, even then I'd probably queue socket peeking. – ThisHandleNotInUse May 17 '15 at 05:55
  • Alright your aim seems to be totally different, you are not using threads for any optimization or speed, but still I would maintain you would not be able to increase the load on your application beyond number of system cores, as these are the only threads which go in parallel, rest all load is on OS and increasing stack allocation, not the application that you are testing. Default stack size on windows is 1 MB, which can surely be changed using some boot config, you may want to deploy the application on a system with very high number of cores and test run the test, then it will be truly loaded – Mrinal Kamboj May 18 '15 at 04:35
  • I will certainly keep in mind what you've talked about for something that is going to be compiled and utilized in an actual application. This was just an attempt to achieve "deadlock" on my singleton to see if there might be an error in my thread locks which are somewhat convoluted due to the fact that it will drop stuff in a buffer and let the thread fall through if the object is otherwise not open for immediate writing and the thread currently using it will complete buffer operations - so if the singleton is busy, the incoming thread "hands off" its operation to the thread with access. – ThisHandleNotInUse May 18 '15 at 20:48
  • Ergo... instead of having a blocking lock for access where potentially an indefinite number of threads can get stuck waiting for one thread, one thread can get stuck doing the queued operations of the otherwise locked out threads while they can freely go about their business. – ThisHandleNotInUse May 18 '15 at 20:55
  • This is just a logging console singleton that can function as a console and/or disk logging - I'm just trying to test it for multi-thread access. I have not nor ever will use 100 threads in any applications I have written or will write. All the above code is is code written exclusively for testing the singleton. The goal is not to make sure the singleton performs fast, but to make sure it doesn't deadlock threads or throw exceptions when multiple threads attempt to write to it at once. – ThisHandleNotInUse May 18 '15 at 21:05
  • Testing deadlock between threads is not always an easy task, since you want to schedule threads such that they block each other, you may post your code if there's an issue in achieving it, difficult to get the complete picture by mere comments – Mrinal Kamboj May 19 '15 at 04:33