0

i am developing a program which lets you pick multiple xml files and sends it to a webserver.

Each xml file data sent would be running on a thread so the data sending runs in parallel. The problem i have is that when i enter the first file it responds early without even entering the next couple of files.

I dont know how to make all the threads wait until i type start to send all of them at once on individual threads.I tried a implementation below but it responds too early after i type the first file.

Heres my code:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;

namespace XMLSender
{
    class Program
    {
        private static string serverUrl;

        static void Main(string[] args)
        {
            Console.WriteLine("Please enter the URL to send the XML File");
            serverUrl = Console.ReadLine();
            List<Thread> threads = new List<Thread>();

            string fileName = "";
            do
            {
                Console.WriteLine("Please enter the XML File you Wish to send");
                fileName = Console.ReadLine();
                Thread t = new Thread(new ParameterizedThreadStart(send));               
                threads.Add(t);
            }
            while (fileName != "start"); //Ends if user enters an empty line
            foreach (Thread t in threads)
            {
                t.Start();
            }
            foreach (Thread t in threads)
            {
                t.Join();
            }

        }
        static private void send(object data)
        {
            try
            {
                //ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(serverUrl);
                byte[] bytes;

                //Load XML data from document 
                XmlDocument doc = new XmlDocument();
                doc.Load((string)data);
                string xmlcontents = doc.InnerXml;

                //Send XML data to Webserver
                bytes = Encoding.ASCII.GetBytes(xmlcontents);
                request.ContentType = "text/xml; encoding='utf-8'";
                request.ContentLength = bytes.Length;
                request.Method = "POST";
                Stream requestStream = request.GetRequestStream();
                requestStream.Write(bytes, 0, bytes.Length);
                requestStream.Close();

                // Get response from Webserver
                HttpWebResponse response;
                response = (HttpWebResponse)request.GetResponse();
                Stream responseStream = response.GetResponseStream();
                string responseStr = new StreamReader(responseStream).ReadToEnd();
                Console.Write(responseStr + Environment.NewLine);

            }
            catch (Exception e)
            {
                Console.WriteLine("An Error Occured" + Environment.NewLine + e);
                Console.ReadLine();
            }
        }
    }
}
Freon
  • 47
  • 1
  • 11
  • 2
    Use Task and not Thread: http://stackoverflow.com/questions/13429129/task-vs-thread-differences Example: http://stackoverflow.com/a/26588973/47351 – RvdK Aug 02 '16 at 09:24
  • What do you mean with "it responds"? You use this phrase twice, but I do not understand what "it" is and what that response would be. – Martin Mulder Aug 02 '16 at 09:25
  • The response comes from the server too fast since its in the main, the response part. under the //get response part – Freon Aug 02 '16 at 09:31
  • Please do not modify your question with the answers given, because that would make the people who answer your question look like idiots since it looks like they are repeating (parts of) your question. – Martin Mulder Aug 02 '16 at 09:47
  • @MartinMulder Do not hesitate to rollback any edit that invalidates existing answer. – Kyll Aug 02 '16 at 09:49
  • OH ill rollback sorry! – Freon Aug 02 '16 at 10:01

3 Answers3

2

If you want to start all threads at once, in your do-while loop, dont start the threads yet. (Dont call t.Start())

Instead, save the file names in a list in the meantime and then, after the while loop, place another foreach loop, which then starts all the threads:

string fileName = "";
List<string> names = new List<string>();
do
{
    Console.WriteLine("Please enter the XML File you Wish to send");
    fileName = Console.ReadLine();
    if(fileName != "start") 
    {
        Thread t = new Thread(new ParameterizedThreadStart(send));               
        threads.Add(t);
        names.Add(fileName);
    }
}
while (fileName != "start"); 

foreach (Thread t in threads)
{
    t.Start(names[0]);
    names.RemoveAt(0);
}

Edit: I added a check to be sure that you don't add another thread when you enter start, as the loop will still finish even if the loop condition isn't true anymore.
Without the check a new thread would be added add the end with startas the Parameter.

TheCodingDamian
  • 443
  • 2
  • 12
  • Alright so i did it, (updated code) and i get this exception An Error Occured System.ArgumentNullException: Value cannot be null. Parameter name: url – Freon Aug 02 '16 at 09:36
  • Your right, I totally forgot about that. I'll edit my answer – TheCodingDamian Aug 02 '16 at 09:37
  • For some reason it thinks start is a file and i get an exception, but i get the response for the actual file though, Going to update code to reflect on it. – Freon Aug 02 '16 at 09:51
  • Alright, One more surprise bug though, it seems that the transport connection is shared so it sends one and receives a response but the other xml file sends and doesnt receive a response but and exception saying the connection was forcibly closed – Freon Aug 02 '16 at 09:56
  • I had this before without multithreading but in a while true loop, (just sending xml in a while loop), which i mean that i recreated that connection everytime, am i reusing that connection on all threads rather than creating one every time? I still want to enter the address once though and let the program create the connection everytime time on each thread. – Freon Aug 02 '16 at 09:58
  • To be honest, I don't really know much about those Webserver classes, so I can't really help you with that. Maybe somebody else has an idea... – TheCodingDamian Aug 02 '16 at 10:03
2

First, create a ManulResetEvent. This event can make threads wait till a certain event happens. The ManulResetEvent is then triggered when all threads are allowes to continue. Like this:

class Program
{
    private static readonly ManualResetEvent _wait = new ManualResetEvent(false);
    ...

In your thread code, let all threads wait:

static private void send(object data)
{
    _wait.WaitOne();
    ...

In your Main-method trigger the event after entering all files:

...
while (fileName != ""); //Ends if user enters an empty line
_wait.Set()
foreach (Thread t in threads)
    ...
Martin Mulder
  • 12,642
  • 3
  • 25
  • 54
  • @vidstige: Can you elaborate. Because so far I agree: The thread will continue when it is started and allowed to continue. My assumption of the problem is that the main thread is asking input from the user (using the Console) while the second thread is writing all responses to that same Console, make it hard for the user. This way the threads only start (and spit out information to the console) after the user is finished inputing filenames. – Martin Mulder Aug 02 '16 at 09:38
  • ah, you're right. I read too quickly and got the impression there where more threads involved. – vidstige Aug 02 '16 at 09:44
1

A second solution could be initializing the thread without starting them, but WITH the filename-parameter:

...
filename = Console.ReadLine();
Thread t = new Thread(() => send(filename));
threads.Add(t);
...

And outside your loop, you start all the threads:

foreach (Thread t in threads)
{
    t.Start();
}
Martin Mulder
  • 12,642
  • 3
  • 25
  • 54
  • Ah i get the same exception from above: System.ArgumentException: The URL cannot be empty. Parameter name: url at System.Xml.XmlTextReaderImpl..ctor(String url, XmlNameTable nt) at System.Xml.XmlDocument.Load(String filename) – Freon Aug 02 '16 at 09:40
  • It can be possible, that when it runs the main program in the try/catch that it doesnt know the url BUT i state it at the top to be it already. Strange... – Freon Aug 02 '16 at 09:44
  • That is why you have to provide the filename to the thread as stated in my answer. – Martin Mulder Aug 02 '16 at 09:46