0

So it takes too long, if I try to do this code in serialization. So I want to do it with threading. But I'm running into safety-problems. Here is what starts the threads:

protected void Page_LoadComplete(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        using (var finished = new CountdownEvent(1))
        {
            for (int i = 1; i <= Convert.ToInt32(ViewState["Count"]); i++)
            {
                string k = i.ToString();
                ThreadInfo threadInfo = new ThreadInfo();
                threadInfo.f = k;
                finished.AddCount(); // Indicate that there is another work item.
                ThreadPool.QueueUserWorkItem(
                   (state) =>
                   {
                       try
                       {
                           Debug.WriteLine("Thread-Start Part " + k.ToString());
                           CalcThread(threadInfo);
                       }
                       finally
                       {
                           finished.Signal(); // Signal that the work item is complete.
                       }
                   }, null);
                Thread.Sleep(300);
            }
            Debug.WriteLine("Waiting till threads are done! ");
            finished.Signal(); // Signal that queueing is complete.
            finished.Wait(); // Wait for all work items to complete.
            Debug.WriteLine("Threads have completed! ");  
    }
}

Here is one place that I believe I'm getting some unsafe conditions, and I really don't know if there is a way to solve the problems. I cannot Lock ( ... ) all the code. Because that would defeat the purpose. So a lot of the calculations happen in a sub-class. The problem, I believe is when this sub-class is called, in multiple threads... the answers I'm getting back are often the same answers.

m_calculation.StartQuotePart(runnerweight, waste, machinesize, injectioncycle, volumefactor, MOQ);

m_calculation.partCostNoShiping = m_calculation.partCostNoShiping / partquantity * (1.0 + partcommission);
m_calculation.FedExShippingCost = m_calculation.FedExShippingCost / partquantity * (1.0 + partcommission);
m_calculation.DHLShippingCost = m_calculation.DHLShippingCost / partquantity * (1.0 + partcommission);
m_calculation.UPSShippingCost = m_calculation.UPSShippingCost / partquantity * (1.0 + partcommission);
m_calculation.OceanShippingCost = m_calculation.OceanShippingCost / partquantity * (1.0 + partcommission);

m_calculation.materialcost_out = m_calculation.materialcost_out / partquantity;
m_calculation.ProcessCost_out = m_calculation.ProcessCost_out / partquantity;
m_calculation.DHLshippingcost_out = m_calculation.DHLshippingcost_out / partquantity;

I have an instance for m_calculation... in the same class that is kicking off the threads. Is there a better way to access it, that wouldn't cause the issues. Is there a better way to create the threads that would cause the variable-mis-mash? This is supposed to run during the 'Page-Load-Complete' and then wait for the threads to complete with 'Finish-Wait'

Edit: I'm updating the Load-Complete with this based on jjxtra's post...

        int count = Convert.ToInt32(ViewState["Count"]);
        var tasks = new Task[count];
        for (int i = 1; i <= count; i++)
        {
            string k = i.ToString();
            ThreadInfo threadInfo = new ThreadInfo();
            threadInfo.f = k;
            tasks[i - 1] = Task.Factory.StartNew(() =>
            {
                CalcThread(threadInfo);
            });
        }
        Task.WaitAll(tasks);
jjxtra
  • 20,415
  • 16
  • 100
  • 140
Robert Koernke
  • 436
  • 3
  • 18
  • Can you post the code in calcThread please? – Ben Dec 16 '20 at 17:03
  • 1
    If you have time for reading, here is a great resource about multithreading: [Threading in C#](http://www.albahari.com/threading/) by Joseph Albahari. There are some basic concepts you need to know, before being able to do multithreading correctly and confidently. – Theodor Zoulias Dec 16 '20 at 17:12
  • I was super-frustrated, and spending far too much time, but I believe I just solved it. I needed to not have a class-wide instance of m_calculation. I also pass m_calculation. – Robert Koernke Dec 16 '20 at 17:24
  • 1
    Your Task code at the bottom looks correct – jjxtra Dec 16 '20 at 20:26
  • 1
    `Task.Run` is preferable to `Task.Factory.StartNew`, for reasons explained [here](https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html). – Theodor Zoulias Dec 16 '20 at 21:01
  • in the end I changed to .Run – Robert Koernke Jan 13 '21 at 19:40

1 Answers1

2

That's what the state object is for, you can pass an object to the thread, let it modify that object in isolation and then execute some sort of callback at the end of the thread to another method, or you can use the Join method to wait for the work to complete.

Instead of pasing null to QueueUserWorkItem, pass an instance of a class with everything the thread needs to do it's work and report results.

Having said all this, if you switch to using Task everything might be much simpler, but not sure what version of .NET / .NET core you are using...

jjxtra
  • 20,415
  • 16
  • 100
  • 140
  • Speaking of 'Tasks': Just to be clear... In this [link](https://stackoverflow.com/questions/263116/c-waiting-for-all-threads-to-complete) there is an example of a 'task' option, waiting for threads to complete. I don't remember why I didn't try it, or if I did. As you can see, my example looks like another one on that page. Are you saying that I wouldn't have 'variable' issues, if I switched to 'Task'? – Robert Koernke Dec 16 '20 at 19:05
  • 1
    You still have to manager variables per task, it's just simpler code than using threadpool. – jjxtra Dec 16 '20 at 20:25