Adding a multithreading scenario for an application in c#

I have developed an application in c#. The class structure is as follows.

Form1 => The UI form. Has a backgroundworker, processbar, and a "ok" button.

SourceReader, TimedWebClient, HttpWorker, ReportWriter //clases do some work

Controller => Has the all over control. From "ok" button click an instance of this class called "cntrl" is created. This cntrlr is a global variable in Form1.cs. (At the constructor of the Controler I create SourceReader, TimedWebClient,HttpWorker,ReportWriter instances. )

Then I call the RunWorkerAsync() of the background worker. Within it code is as follows.

   private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        int iterator = 1;

        for (iterator = 1; iterator <= this.urlList.Count; iterator++)
        {
            cntrlr.Vmain(iterator-1);
            backgroundWorker1.ReportProgress(iterator);             
        }
    }

At themoment ReportProgress updates the progressbar.
The urlList mentioned above has 1000 of urls. cntlr.Vamin(int i) process the whole process at themoment. I want to give the task to several threads, each one having to process 100 of urls. Though access for other instances or methods of them is not prohibited, access to ReportWriter should be limited to only one thread at a time. I can't find a way to do this. If any one have an idea or an answer, please explain.

Upvotes: 1

Views: 122

Answers (3)

spender
spender

Reputation: 120480

Feeling a little more adventurous? Consider using TPL DataFlow to download a bunch of urls:

var urls = new[]{
    "http://www.google.com",
    "http://www.microsoft.com",
    "http://www.apple.com",
    "http://www.stackoverflow.com"};
var tb = new TransformBlock<string, string>(async url => {
    using(var wc = new WebClient())
    {
        var data = await wc.DownloadStringTaskAsync(url);
        Console.WriteLine("Downloaded : {0}", url);
        return data;
    }
}, new ExecutionDataflowBlockOptions{MaxDegreeOfParallelism = 4});
var ab = new ActionBlock<string>(data => {
    //process your data
    Console.WriteLine("data length = {0}", data.Length);
}, new ExecutionDataflowBlockOptions{MaxDegreeOfParallelism = 1});
tb.LinkTo(ab); //join output of producer to consumer block
foreach(var u in urls)
{
    tb.Post(u);
}
tb.Complete();

Note how you can control the parallelism of each block explicitly, so you can gather in parallel but process without going concurrent (for example).

Just grab it with nuget. Easy.

Upvotes: 0

Snorre
Snorre

Reputation: 954

I think I would have used the ThreadPool, instead of background worker, and given each thread 1, not 100 url's to process. The thread pool will limit the number of threads it starts at once, so you wont have to worry about getting 1000 requests at once. Have a look here for a good example

http://msdn.microsoft.com/en-us/library/3dasc8as.aspx

Upvotes: 1

MoonKnight
MoonKnight

Reputation: 23833

If you do want to restrict multiple threads using the same method concurrently then I would use the Semaphore class to facilitate the required thread limit; here's how...

A semaphore is like a mean night club bouncer, it has been provide a club capacity and is not allowed to exceed this limit. Once the club is full, no one else can enter... A queue builds up outside. Then as one person leaves another can enter (analogy thanks to J. Albahari).

A Semaphore with a value of one is equivalent to a Mutex or Lock except that the Semaphore has no owner so that it is thread ignorant. Any thread can call Release on a Semaphore whereas with a Mutex/Lock only the thread that obtained the Mutex/Lock can release it.

Now, for your case we are able to use Semaphores to limit concurrency and prevent too many threads from executing a particular piece of code at once. In the following example five threads try to enter a night club that only allows entry to three...

class BadAssClub
{
    static SemaphoreSlim sem = new SemaphoreSlim(3);
    static void Main()
    {
        for (int i = 1; i <= 5; i++) 
            new Thread(Enter).Start(i);
    }

    // Enfore only three threads running this method at once.
    static void Enter(int i)
    {
        try
        {
            Console.WriteLine(i + " wants to enter.");
            sem.Wait();
            Console.WriteLine(i + " is in!");
            Thread.Sleep(1000 * (int)i);
            Console.WriteLine(i + " is leaving...");
        }
        finally
        {
            sem.Release();
        }
    }
}

Note, that SemaphoreSlim is a lighter weight version of the Semaphore class and incurs about a quarter of the overhead. it is sufficient for what you require.

I hope this helps.

Upvotes: 1

Related Questions