Results 1 to 14 of 14
  1. #1
    R3DDOT's Avatar
    Join Date
    Dec 2013
    Gender
    male
    Location
    C://Windows/system32
    Posts
    347
    Reputation
    38
    Thanks
    2,366
    My Mood
    Cheerful

    Running multiple threads

    So I'm working on a link farmer, basicly it finds links that hold special items(hidden items that can be bought) by making an HttpWebRequest and reading the response stream.

    I thought working with multiple threads might speed this up, since in theory having multiple threads searching is better than just one.
    But apparently, it seems to slowdown the process.

    Here's the code:

    Code:
    int t_count = GetSearchType(); //don't worry about this.
    
                    for (; t_count >= 0; t_count--)
                        new System.Threading.Thread(
                            () =>
                            {
    
                                for (Search_CurrentID += (int)IncrementNum.Value; Search_CurrentID < Search_MaxID && Running; Search_CurrentID += (int)IncrementNum.Value)
                                {
                                    string link = link_mask + Search_CurrentID.ToString();
                                    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(link);
                                    req.Timeout = 2000;
                                    req.Method = WebRequestMethods.Http.Post;
                                    req.ContentType = "application/x-www-form-urlencoded";
                                    req.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64_R3D) AppleWebKit/537.36 (KHTML, like Gecko) Safari/537.36";
                                    req.ContentLength = 0;
    
                                    using (StreamReader wStream = new StreamReader(req.GetResponse().GetResponseStream()))
                                    {
                                        for (string line = wStream.ReadLine(); !wStream.EndOfStream; line = wStream.ReadLine())
                                        {
                                            if (line.Contains("title story-title"))
                                            {
                                                Links_Searched++;
                                                string title = line.Replace("<h1 class=\"title story-title\">", "").Replace("</h1>", "").Remove(0, " 					".Length - 1);
                                                loglink(link, title);
                                            }
    
                                        }
                                    }
                                }
    
                                Running = false;
                                if (Search_CurrentID >= Search_MaxID && PopupOnCompleteCB.Checked)
                                    notifyIcon.ShowBalloonTip(1000, "Aeria Link Farmer", "Search has been completed!", ToolTipIcon.Info);
                            }
                            ).Start();
                }
    If you can figure out what's wrong with this, I'd be really appreciated.

  2. #2
    SteamThief's Avatar
    Join Date
    Nov 2014
    Gender
    male
    Posts
    131
    Reputation
    82
    Thanks
    17
    My Mood
    Amused
    What is the problem you are having, don't you get the other threads to run or does it just not run as fast as expected?

  3. #3
    R3DDOT's Avatar
    Join Date
    Dec 2013
    Gender
    male
    Location
    C://Windows/system32
    Posts
    347
    Reputation
    38
    Thanks
    2,366
    My Mood
    Cheerful
    Quote Originally Posted by SteamThief View Post
    What is the problem you are having, don't you get the other threads to run or does it just not run as fast as expected?
    From what I've seen, it seems to run the all threads. But they run slower, and I'm not sure if that's caused because of the http request(10 requests in 1 sec ==> lag) or if I did something wrong.

  4. #4
    SteamThief's Avatar
    Join Date
    Nov 2014
    Gender
    male
    Posts
    131
    Reputation
    82
    Thanks
    17
    My Mood
    Amused
    What CPU do you have?
    How many cores / Threads does it have?

    I'll take a closer look to the at the code when i'm home.

  5. #5
    R3DDOT's Avatar
    Join Date
    Dec 2013
    Gender
    male
    Location
    C://Windows/system32
    Posts
    347
    Reputation
    38
    Thanks
    2,366
    My Mood
    Cheerful
    Quote Originally Posted by SteamThief View Post
    What CPU do you have?
    How many cores / Threads does it have?

    I'll take a closer look to the at the code when i'm home.
    I got an i7(2.8 GHz). The problem isn't with threads apparently, it's with the requests. If I send too many at once, it lags. I don't think there's a way to get around that, unless by decreasing their amount(like to 3~5 threads).

  6. #6
    SteamThief's Avatar
    Join Date
    Nov 2014
    Gender
    male
    Posts
    131
    Reputation
    82
    Thanks
    17
    My Mood
    Amused
    Quote Originally Posted by R3DDOT View Post


    I got an i7(2.8 GHz). The problem isn't with threads apparently, it's with the requests. If I send too many at once, it lags. I don't think there's a way to get around that, unless by decreasing their amount(like to 3~5 threads).
    Like you said, try to decrease the requests, but I find it weird that it lags if you send to many at once. 10 is not that many, but lower and than go higher until it fails. You can use STopwatch to see have many milisecounds each time and than you'll see easy if it goes faster / slower.

  7. #7
    abuckau907's Avatar
    Join Date
    Dec 2012
    Gender
    male
    Location
    other side of the wire
    Posts
    1,342
    Reputation
    162
    Thanks
    239
    My Mood
    Cold
    @OP Normally multi-threading is used to split the work between multiple threads.
    It looks like you're just making 10 (or..t_count) threads all do the exact same work, instead of splitting up the work. (?)

    Since you're inner for() loop is going from some Start_ID to Max_ID ...you could make each thread responsible for a small range of IDs.

    edit: I don't know, but I'm guessing one thread per cpu core is most efficient ...the OS will 'manage' multiple threads for you..and make sure each gets a little run-time on the cpu, but there is over-head when the OS switches between threads.
    You could try more threads per core...2, 4, maybe even 8...but my guess is 1 (maybe 2?) threads per core is most efficient. Again, just a guess.
    Last edited by abuckau907; 12-02-2014 at 03:15 AM.
    'Some things that can be counted, don't matter. And some things that matter, can't be counted' - A.E.
    --
     

    My posts have some inaccuracies/are wrong/wrong keyword(s) used.
    They're (maybe) pretty close, and I hope they helped you, not created confusion. Take with grain of salt.

    -if you give rep, please leave a comment, else it means less.

  8. The Following User Says Thank You to abuckau907 For This Useful Post:

    R3DDOT (12-06-2014)

  9. #8
    R3DDOT's Avatar
    Join Date
    Dec 2013
    Gender
    male
    Location
    C://Windows/system32
    Posts
    347
    Reputation
    38
    Thanks
    2,366
    My Mood
    Cheerful
    Quote Originally Posted by abuckau907 View Post
    @OP Normally multi-threading is used to split the work between multiple threads.
    It looks like you're just making 10 (or..t_count) threads all do the exact same work, instead of splitting up the work. (?)

    Since you're inner for() loop is going from some Start_ID to Max_ID ...you could make each thread responsible for a small range of IDs.

    edit: I don't know, but I'm guessing one thread per cpu core is most efficient ...the OS will 'manage' multiple threads for you..and make sure each gets a little run-time on the cpu, but there is over-head when the OS switches between threads.
    You could try more threads per core...2, 4, maybe even 8...but my guess is 1 (maybe 2?) threads per core is most efficient. Again, just a guess.
    I understand what you are saying, but by incrementing its value at the end of each loop, they should be using different values. I might be wrong though.

  10. #9
    abuckau907's Avatar
    Join Date
    Dec 2012
    Gender
    male
    Location
    other side of the wire
    Posts
    1,342
    Reputation
    162
    Thanks
    239
    My Mood
    Cold
    I'm not sure sure I understand you, because we want each thread to be different...to have it's own subsection of ID's to scan.
    If all threads are doing the exact same thing, there is no point; you're just doing the same work multiple times.. (?)

    I mean to create 2 more variables in the outmost for() loop - each thread will get a START_ID and STOP_ID. Increment these 2 variable in the outer for loop, not inside the newly created threads.
    ^^Or however you want to implement "giving each thread a chunk of the IDs." But by this point, you'll have to decided maximum number of threads you'll use.
    Code:
    int start_id = 0
    int stop_id = ****
    for ( ; t_count >=0; t_count--)
    
    {
     spawnThread(start_id,stop_id); 
     start_id = end_id + 1 '' right, lol?
     end_id   += (total_IDS / number_cores) + ****; //something similar to this
    }
    
    ^^obviously a little primitive. Point is, split up the range of ID's you make each thread scan. If the dataset isn't a perfect multiple of your thread_count, some thread will have to do a couple extra/less ID's, which is ok.

    In your code:
    Code:
      ...
      for (Search_CurrentID += (int)IncrementNum.Value; Search_CurrentID < Search_MaxID && Running; Search_CurrentID += (int)IncrementNum.Value)
      ...
    ^^is the part that could be changed. Don't make each thread scan the entire range of data.


    I believe each thread gets a private copy 'Search_CurrentID' ...if not, all threads are trying to update the same variable, which is bad. (can you imagine if all 10 threads executed their For() loop's increment condition (current_id would go up 10x more than it should). I'd add more debugging-output to check. I don't see anything about mutexes or locks (etc?) so I'm assuming each thread gets private copies of any variables used, but I don't know (we don't see how you declare the variables..). I'm guessing it gives each thread a local copy by default, and only share the memory of some variable if you specifically request it. Just guessing though : ( Looking forward to your reply. I might even test it myself soon (you know..have more test sets..I'm on core i3 lol).
    Last edited by abuckau907; 12-02-2014 at 01:00 PM.
    'Some things that can be counted, don't matter. And some things that matter, can't be counted' - A.E.
    --
     

    My posts have some inaccuracies/are wrong/wrong keyword(s) used.
    They're (maybe) pretty close, and I hope they helped you, not created confusion. Take with grain of salt.

    -if you give rep, please leave a comment, else it means less.

  11. The Following User Says Thank You to abuckau907 For This Useful Post:

    R3DDOT (12-06-2014)

  12. #10
    Jason's Avatar
    Join Date
    Apr 2010
    Gender
    male
    Location
    /dev/null
    Posts
    5,704
    Reputation
    918
    Thanks
    7,676
    My Mood
    Mellow
    This is one of the "traps" of multi-threading. Just because you run code in multiple threads doesn't automatically mean you'll experience any sort of speed up. It's important to know that multi-threading your application is NOT some magical solution to increase performance; in a lot of cases it will actually make your code slower if done incorrectly.

    I'd recommend reading up on multi-threading and parallelism. It's a pretty complex topic at first but it's essential you know what you're doing when you start otherwise you're likely to end up getting shafted later on.

    In the mean time, this kind of problem suits a task-based approach. If you're using .NET 4.0+ then I'd highly recommend using the new "Task" class in the System.Threading.Tasks namespace. It abstracts away a lot of the manually thread creation/synchronisation and makes it a lot easier to do concurrent work.

    Next, add the following class to your project. It's mostly a direct copy from an article from MSDN with some minor adjustments on my part:

    Code:
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Collections.Generic;
    
    namespace JLibrary.Net {
        public class SparseScheduler : TaskScheduler {
            [ThreadStatic]
            private static bool _currentThreadIsProcessingItems;
    
            private readonly LinkedList<Task> _waitingTasks = new LinkedList<Task>();
            private readonly int _maxConcurrent;
            private int _runningDelegates;
    
            public SparseScheduler(int maximumConcurrent) {
                if (maximumConcurrent < 1) {
                    throw new ArgumentOutOfRangeException("maximumConcurrent");
                }
    
                _maxConcurrent = maximumConcurrent;
                _runningDelegates = 0;
            }
    
            protected override void QueueTask(Task task) {
                lock (_waitingTasks) {
                    _waitingTasks.AddLast(task);
                    if (_runningDelegates < _maxConcurrent) {
                        ++_runningDelegates;
                        NotifyThreadPool();
                    }
                }
            }
    
            private void NotifyThreadPool() {
                ThreadPool.UnsafeQueueUserWorkItem(_ => {
                    _currentThreadIsProcessingItems = true;
                    try {
                        while (true) {
                            Task item;
                            lock (_waitingTasks) {
                                if (_waitingTasks.Count == 0) {
                                    --_runningDelegates;
                                    break;
                                }
    
                                item = _waitingTasks.First.Value;
                                _waitingTasks.RemoveFirst();
                            }
    
                            base.TryExecuteTask(item);
                        }
                    }
                    finally {
                        _currentThreadIsProcessingItems = false;
                    }
                }, null);
            }
    
            protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) {
                if (!_currentThreadIsProcessingItems) {
                    return false;
                }
    
                return taskWasPreviouslyQueued
                    ? TryDequeue(task) && base.TryExecuteTask(task)
                    : base.TryExecuteTask(task);
            }
    
            protected override bool TryDequeue(Task task) {
                lock (_waitingTasks) {
                    return _waitingTasks.Remove(task);
                }
            }
    
            protected override IEnumerable<Task> GetScheduledTasks() {
                bool lockTaken = false;
                try {
                    Monitor.TryEnter(_waitingTasks, ref lockTaken);
                    if (lockTaken) return _waitingTasks;
                    else throw new NotSupportedException();
                }
                finally {
                    if (lockTaken) {
                        Monitor.Exit(_waitingTasks);
                    }
                }
            }
        }
    }
    This is what's known as a TaskScheduler. Essentially you feed it small bodies of work (tasks) and it will handle all of the scheduling/threading for you. It's important to note that there's no guarantee on which thread your task will be run on, so care must be taken when writing your tasks.

    This particular scheduler is known as a SparseScheduler. I originally had need for it for precisely the situation you're in now: I wanted to run multiple web requests in parallel, but didn't want to spam all of them out at once as this bloats the network unnecessarily and often causes huge amounts of lag. The SparseScheduler only allows a certain amount of jobs to be running at one time. This means I can generate a task for all of my web requests, send them to the scheduler and know that only X will be running at any one time.

    Next, a really simple wrapper class around the SparseScheduler to make it specifically for WebRequests (add this in its own class file)
    Code:
    using System;
    using System.Net;
    using System.Threading;
    using System.Threading.Tasks;
    using JLibrary.Threading;
    
    namespace JLibrary.Net {
        // Weak wrapper around a Task
        public class WebRequestToken {
            private readonly Task _task;
            internal Task Task { get { return _task; } }
    
            internal WebRequestToken(Task task) {
                _task = task;
            }
    
            private WebRequestToken() {
                // Hide the empty constructor
            }
        };
    
        public class WebRequestFactory {
            private readonly TaskFactory _factory;
            private readonly SparseScheduler _scheduler;
            private readonly CancellationTokenSource _cts;
    
            public WebRequestFactory(int maxConcurrentRequests) {
                _scheduler = new SparseScheduler(maxConcurrentRequests);
                _factory = new TaskFactory(_scheduler);
                _cts = new CancellationTokenSource();
            }
    
            public WebRequestToken SendRequest(WebRequest request, Action<Exception,WebResponse> handler) {
                if (request == null)
                    throw new ArgumentNullException("request");
                if (handler == null)
                    throw new ArgumentNullException("handler");
    
                var task = _factory.StartNew(() => {
                    try {
                        using (var response = request.GetResponse())
                            handler.Invoke(null, response);
                    }
                    catch (Exception e) {
                        handler.Invoke(e, null);
                    }
                }, _cts.Token);
    
                return new WebRequestToken(task);
            }
    
            public void WaitAll(params WebRequestToken[] tokens) {
                Task.WaitAll(Array.ConvertAll(tokens, t => t.Task), _cts.Token);
            }
    
            public void WaitAll(WebRequestToken[] tokens, int millisecondsToWait) {
                Task.WaitAll(Array.ConvertAll(tokens, t => t.Task), millisecondsToWait, _cts.Token);
            }
        }
    }
    Finally, onto your actual code. Add the reference to the correct namespace to your code:
    Code:
    using JLibrary.Net;
    And now your code becomes very simple. First define a function to process a response:
    Code:
    private void HandleResponse(Exception e, WebResponse response) {
        if (e != null) {
            // do something with the exception
            throw e;
        }
    
        // Store a local "searched" counter to avoid locking the mutex every time a link is found
        int searched = 0;
        using(var sreader = new StreamReader(response.GetResponseStream())) {
            for(string line = sreader.ReadLine(); line != null; line = sreader.ReadLine()) {
                if (line.Contains("title story-title")) {
                    ++searched;
                    var title = line.Replace("<h1 class=\"title story-title\">", "").Replace("</h1>", "").Trim();
                    loglink(response.ResponseUri.ToString(), title);
                }
            }
        }
    
        lock(_mutex) {
            Links_Searched += searched;
        }
    }
    NOTE: The use of _mutex here. This is just an arbitrary variable to allow for a lock to be entered when incrementing the "Links_Searched" count. Otherwise you'll likely get race conditions.
    Code:
    private readonly object _mutex = new object();
    Will suffice.

    Onto your actual code. I've cleaned it up somewhat but as you only included a small snippet I wasn't actually able to make it as nice as I would have liked.

    Code:
    int maxConcurrent = 8; // Maximum 8 web requests running at any time.
    int increment = (int)IncrementNum.Value;
    
    var factory = new WebRequestFactory(maxConcurrent);
    var tokens = new List<WebRequestToken>();
    
    for(Search_CurrentID+=increment; Search_CurrentID < Search_MaxID; Search_CurrentID+=increment) {
        var link = string.Format("{0}{1}", link_mask, Search_CurrentID);
        var req = (HttpWebRequest)WebRequest.Create(link);
        req.Timeout = 2000;
        req.Method = WebRequestMethods.Http.Post;
        req.ContentType = "application/x-www-form-urlencoded";
        req.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64_R3D) AppleWebKit/537.36 (KHTML, like Gecko) Safari/537.36";
        req.ContentLength = 0;
    
        tokens.Add(factory.SendRequest(req, HandleResponse));
    }
    
    // Now wait for the requests to complete
    factory.WaitAll(tokens.ToArray());
    
    // Done.
    I didn't explain much here so if you have any questions feel free to ask
    Last edited by Jason; 12-04-2014 at 08:21 PM. Reason: Words

    Quote Originally Posted by Jeremy S. Anderson
    There are only two things to come out of Berkley, Unix and LSD,
    and I don’t think this is a coincidence
    You can win the rat race,
    But you're still nothing but a fucking RAT.


    ++Latest Projects++
    [Open Source] Injection Library
    Simple PE Cipher
    FilthyHooker - Simple Hooking Class
    CLR Injector - Inject .NET dlls with ease
    Simple Injection - An in-depth look
    MPGH's .NET SDK
    eJect - Simple Injector
    Basic PE Explorer (BETA)

  13. The Following 2 Users Say Thank You to Jason For This Useful Post:

    [MPGH]Mayion (12-05-2014),R3DDOT (12-05-2014)

  14. #11
    R3DDOT's Avatar
    Join Date
    Dec 2013
    Gender
    male
    Location
    C://Windows/system32
    Posts
    347
    Reputation
    38
    Thanks
    2,366
    My Mood
    Cheerful
    Quote Originally Posted by Jason View Post
    This is one of the "traps" of multi-threading. Just because you run code in multiple threads doesn't automatically mean you'll experience any sort of speed up. It's important to know that multi-threading your application is NOT some magical solution to increase performance; in a lot of cases it will actually make your code slower if done incorrectly.

    I'd recommend reading up on multi-threading and parallelism. It's a pretty complex topic at first but it's essential you know what you're doing when you start otherwise you're likely to end up getting shafted later on.

    In the mean time, this kind of problem suits a task-based approach. If you're using .NET 4.0+ then I'd highly recommend using the new "Task" class in the System.Threading.Tasks namespace. It abstracts away a lot of the manually thread creation/synchronisation and makes it a lot easier to do concurrent work.

    Next, add the following class to your project. It's mostly a direct copy from an article from MSDN with some minor adjustments on my part:

    Code:
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Collections.Generic;
    
    namespace JLibrary.Net {
        public class SparseScheduler : TaskScheduler {
            [ThreadStatic]
            private static bool _currentThreadIsProcessingItems;
    
            private readonly LinkedList<Task> _waitingTasks = new LinkedList<Task>();
            private readonly int _maxConcurrent;
            private int _runningDelegates;
    
            public SparseScheduler(int maximumConcurrent) {
                if (maximumConcurrent < 1) {
                    throw new ArgumentOutOfRangeException("maximumConcurrent");
                }
    
                _maxConcurrent = maximumConcurrent;
                _runningDelegates = 0;
            }
    
            protected override void QueueTask(Task task) {
                lock (_waitingTasks) {
                    _waitingTasks.AddLast(task);
                    if (_runningDelegates < _maxConcurrent) {
                        ++_runningDelegates;
                        NotifyThreadPool();
                    }
                }
            }
    
            private void NotifyThreadPool() {
                ThreadPool.UnsafeQueueUserWorkItem(_ => {
                    _currentThreadIsProcessingItems = true;
                    try {
                        while (true) {
                            Task item;
                            lock (_waitingTasks) {
                                if (_waitingTasks.Count == 0) {
                                    --_runningDelegates;
                                    break;
                                }
    
                                item = _waitingTasks.First.Value;
                                _waitingTasks.RemoveFirst();
                            }
    
                            base.TryExecuteTask(item);
                        }
                    }
                    finally {
                        _currentThreadIsProcessingItems = false;
                    }
                }, null);
            }
    
            protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) {
                if (!_currentThreadIsProcessingItems) {
                    return false;
                }
    
                return taskWasPreviouslyQueued
                    ? TryDequeue(task) && base.TryExecuteTask(task)
                    : base.TryExecuteTask(task);
            }
    
            protected override bool TryDequeue(Task task) {
                lock (_waitingTasks) {
                    return _waitingTasks.Remove(task);
                }
            }
    
            protected override IEnumerable<Task> GetScheduledTasks() {
                bool lockTaken = false;
                try {
                    Monitor.TryEnter(_waitingTasks, ref lockTaken);
                    if (lockTaken) return _waitingTasks;
                    else throw new NotSupportedException();
                }
                finally {
                    if (lockTaken) {
                        Monitor.Exit(_waitingTasks);
                    }
                }
            }
        }
    }
    This is what's known as a TaskScheduler. Essentially you feed it small bodies of work (tasks) and it will handle all of the scheduling/threading for you. It's important to note that there's no guarantee on which thread your task will be run on, so care must be taken when writing your tasks.

    This particular scheduler is known as a SparseScheduler. I originally had need for it for precisely the situation you're in now: I wanted to run multiple web requests in parallel, but didn't want to spam all of them out at once as this bloats the network unnecessarily and often causes huge amounts of lag. The SparseScheduler only allows a certain amount of jobs to be running at one time. This means I can generate a task for all of my web requests, send them to the scheduler and know that only X will be running at any one time.

    Next, a really simple wrapper class around the SparseScheduler to make it specifically for WebRequests (add this in its own class file)
    Code:
    using System;
    using System.Net;
    using System.Threading;
    using System.Threading.Tasks;
    using JLibrary.Threading;
    
    namespace JLibrary.Net {
        // Weak wrapper around a Task
        public class WebRequestToken {
            private readonly Task _task;
            internal Task Task { get { return _task; } }
    
            internal WebRequestToken(Task task) {
                _task = task;
            }
    
            private WebRequestToken() {
                // Hide the empty constructor
            }
        };
    
        public class WebRequestFactory {
            private readonly TaskFactory _factory;
            private readonly SparseScheduler _scheduler;
            private readonly CancellationTokenSource _cts;
    
            public WebRequestFactory(int maxConcurrentRequests) {
                _scheduler = new SparseScheduler(maxConcurrentRequests);
                _factory = new TaskFactory(_scheduler);
                _cts = new CancellationTokenSource();
            }
    
            public WebRequestToken SendRequest(WebRequest request, Action<Exception,WebResponse> handler) {
                if (request == null)
                    throw new ArgumentNullException("request");
                if (handler == null)
                    throw new ArgumentNullException("handler");
    
                var task = _factory.StartNew(() => {
                    try {
                        using (var response = request.GetResponse())
                            handler.Invoke(null, response);
                    }
                    catch (Exception e) {
                        handler.Invoke(e, null);
                    }
                }, _cts.Token);
    
                return new WebRequestToken(task);
            }
    
            public void WaitAll(params WebRequestToken[] tokens) {
                Task.WaitAll(Array.ConvertAll(tokens, t => t.Task), _cts.Token);
            }
    
            public void WaitAll(WebRequestToken[] tokens, int millisecondsToWait) {
                Task.WaitAll(Array.ConvertAll(tokens, t => t.Task), millisecondsToWait, _cts.Token);
            }
        }
    }
    Finally, onto your actual code. Add the reference to the correct namespace to your code:
    Code:
    using JLibrary.Net;
    And now your code becomes very simple. First define a function to process a response:
    Code:
    private void HandleResponse(Exception e, WebResponse response) {
        if (e != null) {
            // do something with the exception
            throw e;
        }
    
        // Store a local "searched" counter to avoid locking the mutex every time a link is found
        int searched = 0;
        using(var sreader = new StreamReader(response.GetResponseStream())) {
            for(string line = sreader.ReadLine(); line != null; line = sreader.ReadLine()) {
                if (line.Contains("title story-title")) {
                    ++searched;
                    var title = line.Replace("<h1 class=\"title story-title\">", "").Replace("</h1>", "").Trim();
                    loglink(response.ResponseUri.ToString(), title);
                }
            }
        }
    
        lock(_mutex) {
            Links_Searched += searched;
        }
    }
    NOTE: The use of _mutex here. This is just an arbitrary variable to allow for a lock to be entered when incrementing the "Links_Searched" count. Otherwise you'll likely get race conditions.
    Code:
    private readonly object _mutex = new object();
    Will suffice.

    Onto your actual code. I've cleaned it up somewhat but as you only included a small snippet I wasn't actually able to make it as nice as I would have liked.

    Code:
    int maxConcurrent = 8; // Maximum 8 web requests running at any time.
    int increment = (int)IncrementNum.Value;
    
    var factory = new WebRequestFactory(maxConcurrent);
    var tokens = new List<WebRequestToken>();
    
    for(Search_CurrentID+=increment; Search_CurrentID < Search_MaxID; Search_CurrentID+=increment) {
        var link = string.Format("{0}{1}", link_mask, Search_CurrentID);
        var req = (HttpWebRequest)WebRequest.Create(link);
        req.Timeout = 2000;
        req.Method = WebRequestMethods.Http.Post;
        req.ContentType = "application/x-www-form-urlencoded";
        req.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64_R3D) AppleWebKit/537.36 (KHTML, like Gecko) Safari/537.36";
        req.ContentLength = 0;
    
        tokens.Add(factory.SendRequest(req, HandleResponse));
    }
    
    // Now wait for the requests to complete
    factory.WaitAll(tokens.ToArray());
    
    // Done.
    I didn't explain much here so if you have any questions feel free to ask
    Actually I've never heard of that class, TaskScheduler.
    I'll definitely check it out later, I'm kinda busy atm. Thanks for the explanation, really helpful.

  15. #12
    Mayion's Avatar
    Join Date
    Oct 2012
    Gender
    male
    Location
    Bed
    Posts
    13,504
    Reputation
    4018
    Thanks
    8,373
    My Mood
    Twisted
    Quote Originally Posted by R3DDOT View Post


    Actually I've never heard of that class, TaskScheduler.
    I'll definitely check it out later, I'm kinda busy atm. Thanks for the explanation, really helpful.
    Do I close the thread and mark it as solved?
    I do not use any type of messenger outside of MPGH.
    Inactive but you can reach me through VM/PM.










     

    Donator - 30 August 2013
    Battlefield Minion - 26 October 2013

    Blackshot Minion - 14 January 2014/16 September 2014
    Minecraft Minion - 7 February 2014/16 September 2014
    WarRock Minion - 23 February 2014
    League of Legends Minion - 21 March 2014

    Minion+ - 15 May 2014
    Other Semi-Popular First Person Shooter Minion - 8 August 2014
    CrossFire Minion - 23 October 2014
    Programming Section Minion - 13 November 2014
    Marketplace Minion - 7 December 2014

    Official Middleman - 7 December 2014 - 27 June 2015
    Moderator - 29 December 2014
    Project Blackout Minion - 10 January 2015
    News Force Interviewer - January 2015
    Steam Games Minion - 21 March 2015
    Dragon Nest Minion - 31 March 2015
    Publicist - April 2015 - 21 September 2015
    Global Moderator - 25 August 2015
    Super User - 13 August 2016



  16. The Following User Says Thank You to Mayion For This Useful Post:

    R3DDOT (12-06-2014)

  17. #13
    R3DDOT's Avatar
    Join Date
    Dec 2013
    Gender
    male
    Location
    C://Windows/system32
    Posts
    347
    Reputation
    38
    Thanks
    2,366
    My Mood
    Cheerful
    Quote Originally Posted by Mayion View Post


    Do I close the thread and mark it as solved?
    Yeah, you can mark as solved. Thanks.

  18. #14
    Mayion's Avatar
    Join Date
    Oct 2012
    Gender
    male
    Location
    Bed
    Posts
    13,504
    Reputation
    4018
    Thanks
    8,373
    My Mood
    Twisted
    Glad you found a solution.
    Closed.
    I do not use any type of messenger outside of MPGH.
    Inactive but you can reach me through VM/PM.










     

    Donator - 30 August 2013
    Battlefield Minion - 26 October 2013

    Blackshot Minion - 14 January 2014/16 September 2014
    Minecraft Minion - 7 February 2014/16 September 2014
    WarRock Minion - 23 February 2014
    League of Legends Minion - 21 March 2014

    Minion+ - 15 May 2014
    Other Semi-Popular First Person Shooter Minion - 8 August 2014
    CrossFire Minion - 23 October 2014
    Programming Section Minion - 13 November 2014
    Marketplace Minion - 7 December 2014

    Official Middleman - 7 December 2014 - 27 June 2015
    Moderator - 29 December 2014
    Project Blackout Minion - 10 January 2015
    News Force Interviewer - January 2015
    Steam Games Minion - 21 March 2015
    Dragon Nest Minion - 31 March 2015
    Publicist - April 2015 - 21 September 2015
    Global Moderator - 25 August 2015
    Super User - 13 August 2016



Similar Threads

  1. [Solved] running multiple threads at once?
    By myamazingcat in forum Call of Duty Modern Warfare 2 GSC Modding Help/Discussion
    Replies: 1
    Last Post: 06-13-2011, 11:56 PM
  2. Running Multiple Clients
    By TonicStyle in forum League of Legends Help
    Replies: 10
    Last Post: 06-10-2011, 02:35 PM
  3. [Help] How do I inject a dll and run a thread safely not from dllmain?
    By PsychicSounds in forum C++/C Programming
    Replies: 2
    Last Post: 06-05-2011, 09:15 PM
  4. [Help] Cannot run multiple instances of SoldierFront(2)
    By Snake in forum Soldier Front General
    Replies: 23
    Last Post: 07-07-2010, 03:51 PM
  5. Can i run multiple combat arms accounts from same computer at the same time?
    By ashaseraph in forum Combat Arms Hacks & Cheats
    Replies: 6
    Last Post: 08-06-2009, 02:11 PM