user3597487
user3597487

Reputation: 41

How to add strings without any problems using parallel foreach?

Here is my current code:

Parallel.ForEach(Arguments, Argument =>
{
    if (Argument != Command_Name)
    {
        WebRequest web_request = WebRequest.Create("https://www.aol.com/?command=1&domain=" + Argument);
        web_request.Timeout = 5000;
        ((HttpWebRequest)web_request).UserAgent = "Mozilla Firefox 5.0";
        HttpWebResponse web_response = (HttpWebResponse)web_request.GetResponse();
        StreamReader response = new StreamReader(web_response.GetResponseStream(), Encoding.UTF8);
        Message += Argument + " => " + response.ReadToEnd() + Environment.NewLine;
    }
});

This code does not work correctly, I am looking for a SMALL alternative that does. This code returns some of the arguments in the Message string... What is a good way to multi-threaded string addition? That is what I need.

More info: The Message string will return a, b, and c sometimes while others it will only return just a or b...

I appreciate any help on this, thanks!

Upvotes: 1

Views: 3998

Answers (5)

fubo
fubo

Reputation: 45997

You can use a concurrent collection and make that afterwards to one string

var ThreadSafeList = new System.Collections.Concurrent.ConcurrentBag<string>();
Parallel.ForEach(Arguments, Argument =>
{
    if (Argument != Command_Name)
    {
        WebRequest web_request = WebRequest.Create("https://www.aol.com/?command=1&domain=" + Argument);
        web_request.Timeout = 5000;
        ((HttpWebRequest)web_request).UserAgent = "Mozilla Firefox 5.0";
        HttpWebResponse web_response = (HttpWebResponse)web_request.GetResponse();
        StreamReader response = new StreamReader(web_response.GetResponseStream(), Encoding.UTF8);
        ThreadSafeList.Add(Argument + " => " + response.ReadToEnd());                    
    }
});
string Result = string.Join("", ThreadSafeList);

Or try to lock the Item you want to write.

StringBuilder Message = new StringBuilder();
Parallel.ForEach(Arguments, Argument =>
{
    if (Argument != Command_Name)
    {
        WebRequest web_request = WebRequest.Create("https://www.aol.com/?command=1&domain=" + Argument);
        web_request.Timeout = 5000;
        ((HttpWebRequest)web_request).UserAgent = "Mozilla Firefox 5.0";
        HttpWebResponse web_response = (HttpWebResponse)web_request.GetResponse();
        StreamReader response = new StreamReader(web_response.GetResponseStream(), Encoding.UTF8);
        lock(Message) //locking here
        {
           Message.AppendLine(Argument + " => " + response.ReadToEnd());
        }
    }
});

Upvotes: 0

dovid
dovid

Reputation: 6481

for collect elements AsParallel() its more appropriate:

var lines = Arguments.AsParallel().Select(Argument =>
{
    if (Argument == Command_Name) return null;

    WebRequest web_request = WebRequest.Create("https://www.aol.com/?command=1&domain=" + Argument);
    web_request.Timeout = 5000;
    ((HttpWebRequest)web_request).UserAgent = "Mozilla Firefox 5.0";
    HttpWebResponse web_response = (HttpWebResponse)web_request.GetResponse();
    StreamReader response = new StreamReader(web_response.GetResponseStream(), Encoding.UTF8);

    return Argument + " => " + response.ReadToEnd();
}).ToArray();

var concate = string.Join(Environment.NewLine, lines);

Upvotes: 1

David
David

Reputation: 10708

Implementing thread safety in this case wuold probably best involve the lock statement. Remember never to lock on an object you don't have exclusive access to, which in this case cabe method-scoped.

object key = new object(); // Empty object serves lightest for locks
Parallel.ForEach(Arguments, Argument =>
{
    if (Argument != Command_Name)
    {
        WebRequest web_request = WebRequest.Create("https://www.aol.com/?command=1&domain=" + Argument);
        web_request.Timeout = 5000;
        ((HttpWebRequest)web_request).UserAgent = "Mozilla Firefox 5.0";
        HttpWebResponse web_response = (HttpWebResponse)web_request.GetResponse();
        StreamReader response = new StreamReader(web_response.GetResponseStream(), Encoding.UTF8);

        lock(key)
            Message += Argument + " => " + response.ReadToEnd() + Environment.NewLine;
    }
});

Upvotes: -1

ilivewithian
ilivewithian

Reputation: 19702

You could just lock around the Message += part:

//scoped to the same scope as the Message object
private Object thisLock = new Object();

var textResult = response.ReadToEnd() + Environment.NewLine;
lock(thisLock)
{
    Message += Argument + " => " + textResult;
}

Upvotes: 1

Selman Gen&#231;
Selman Gen&#231;

Reputation: 101701

You can use a thread-safe collection to store the messages such as ConcurrentBag<T>.Then just make the concetanation after the loop:

var messages = new ConcurrentBag<string>();
Parallel.ForEach(Arguments, Argument =>
{
   ... 
   messages.Add(Argument + " => " + response.ReadToEnd());
}
var result = string.Join(Environment.NewLine, messages);

Upvotes: 9

Related Questions