简体   繁体   中英

C# multi-threading program with WebRequest

First I am new in the forum so please have a little bit patience with me and my english. :-)

I am writing a C# application which should send multithreaded SOAP requests to an apache backend. Everything worked fine till now but I ran into a problem. The application first reads a XML file from another system that is parsed first into classes, sorted and send to the SOAP backend. Here the snippet

List<Thread> ThreadsPerOneRecord = new List<Thread>();         
bool ExecuteSingleThreaded = false;
//The variable list is passed as parameter to the function 

foreach (Record prov in list)
{
  XMLResult.AppendText("Type: " + prov.Type + Environment.NewLine);

  Thread t = new Thread(() => Send(prov, c));                                
  t.Start();
  //Here the sleep 
  Thread.Sleep(50);
  ThreadsPerOneRecord.Add(t);               

  #region Code for test single threaded execution
  if (ExecuteSingleThreaded)
  {
    foreach (Thread t2 in ThreadsPerOneRecord)
      t2.Join();
    ThreadsPerOneRecord.Clear();
  }
  #endregion
}

XMLResult.AppendText("Waiting for the threads to finish" + Environment.NewLine);
//Waiting for the threads to finish
foreach (Thread t in ThreadsPerOneRecord)            
  t.Join(); 

As I am sending this to the SOAP web service it works fine except one request. These requests are mixed up between each other. Ie:

What it should be: 
Record 1 -> SOAP
Record 2 -> SOAP
Record 3 -> SOAP

What it is
Record 1 -> SOAP
Record 2 -> SOAP 
Record 2 -> SOAP 
Record 3 -> nowhere

I already tried to debug the whole code and with the debugger it works fine. The same when I insert this sleep of 50 milliseconds. But without the sleep it mixes this two records ...

Does anybody have any idea why this happens? Shouldn't every thread be independent from itself? Also I checked the collection and the data are correctly inside.

Thanks

Oldfighter

Replace

Thread t = new Thread(() => Send(prov, c));
t.Start();

with

Thread t = new Thread(item => Send(item, c));
t.Start(prov);

In your code lambda expression actually sees changes to iterator variable (it's the same variable for every thread, not value captured when you passed lambda to thread constructor).

your problem is that you are accessing a modified closure in your Foreach loop

here is the fix:

        List<Thread> ThreadsPerOneRecord = new List<Thread>();
        bool ExecuteSingleThreaded = false;
        //The variable list is passed as parameter to the function  

        foreach (Record prov in list)
        {
            var tempProv = prov;
            XMLResult.AppendText("Type: " + tempProv.Type + Environment.NewLine);

            Thread t = new Thread(() => Send(tempProv, c));
            t.Start();
            //Here the sleep  
            Thread.Sleep(50);
            ThreadsPerOneRecord.Add(t);

            #region Code for test single threaded execution
            if (ExecuteSingleThreaded)
            {
                foreach (Thread t2 in ThreadsPerOneRecord)
                    t2.Join();
                ThreadsPerOneRecord.Clear();
            }
            #endregion
        }

        XMLResult.AppendText("Waiting for the threads to finish" + Environment.NewLine);
        //Waiting for the threads to finish 
        foreach (Thread t in ThreadsPerOneRecord)
            t.Join(); 

Classic foreach/capture; fix is easy - add an extra variable:

foreach (Record tmp in list)
{
  Record prov = tmp;
  XMLResult.AppendText("Type: " + prov.Type + Environment.NewLine);

  Thread t = new Thread(() => Send(prov, c));                      

Otherwise, "prov" is shared between all the lambdas. It has been publicly mentioned that fixing this is being evaluated for c# 5, but not yet confirmed either way.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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