1

I have a function, like this,

private void Step()
{
    foreach (A a in aList)
        a.Act();
    foreach (B b in bList)
        b.Act();
    foreach (C c in cList)
        c.Act();
}

where "a" gets the first chance "b" next and "c" gets the last chance,

I want a way where every one gets equal chance how do i do it in C#?

Thanks a lot for all the help!!!

  • 5
    What do you mean by equal chance? You just mean you want them to all run at the same time? – clamchoda Apr 18 '11 at 18:12
  • I was going to say use threading, but Brook has an awesome implementation posted as an answer. – clamchoda Apr 18 '11 at 18:18
  • I'm confused, you want to run them in parallel using multiple threads, or you just want to run them all in one loop? We need some clarification. – Brook Apr 18 '11 at 18:19
  • can someone give me a multi threading example for this? –  Apr 18 '11 at 18:34

3 Answers3

4

I would define an interface IActable (pick your name) which defines Act().

public interface IActable
{
  void Act();
}

Make A, B, and C implement IActable. Then, change your function to do the following.

private void Step()
{
    var all = new List<IActable>();
    all.AddRange(aList);
    all.AddRange(bList);
    all.AddRange(cList);
    all = all.Shuffle(new Random());

    foreach (IActable a in all)
    {
        a.Act();
    }
}

Shuffle method, stolen from here

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{
    T[] elements = source.ToArray();
    // Note i > 0 to avoid final pointless iteration
    for (int i = elements.Length-1; i > 0; i--)
    {
        // Swap element "i" with a random earlier element it (or itself)
        int swapIndex = rng.Next(i + 1);
        T tmp = elements[i];
        elements[i] = elements[swapIndex];
        elements[swapIndex] = tmp;
    }
    // Lazily yield (avoiding aliasing issues etc)
    foreach (T element in elements)
    {
        yield return element;
    }
}

A parallel implementation using plinq. (order not preserved)

private void Step()
    {
        var all = new List<IActable>();
        all.AddRange(aList);
        all.AddRange(bList);
        all.AddRange(cList);

        all.AsParallel().ForAll(a=>a.Act());
    }
Community
  • 1
  • 1
Brook
  • 5,949
  • 3
  • 31
  • 45
  • 3
    In this case items from the original lists will be ordered in the `all` list the same way as in the original `Step()` function. You should reorder/mix items in the `all` list. Or just pick the random item from the `all` list, call `Act()` and remove it from the list until `all` list is not empty. – Elalfer Apr 18 '11 at 18:18
  • I think randomization technique I added to my previous note should be better because it doesn't depend of the items properties. And who knows how to compare different classes even though all of them implement IActable interface. I this case all items will have equal probability. – Elalfer Apr 18 '11 at 18:23
  • @Elalfer: I've seen very few instances in the real world where random is the correct sort order. My gut tells me there's a field here to sort on, but that's just a guess. – Brook Apr 18 '11 at 18:27
  • @Brook: I have a good example. Quick Sort could take `O(n^2)` in the worst case, but by simple mixing elements probability of the worst case could be reduced to almost 0. ;) – Elalfer Apr 18 '11 at 18:31
  • @Elalfer: Well then, I sure hope the OP isn't implementing quick sort :P – Brook Apr 18 '11 at 18:33
  • 1
    A Fisher-Yates shuffle is trivial to implement and it will give O(n) shuffling and would also not require modifying the object to have some `SortCriteria` property to be randomized. – Anthony Pegram Apr 18 '11 at 18:36
  • I love this idea but still i am looking for more options like multi threading too if someone can help me –  Apr 18 '11 at 18:37
  • @softplaza: I think this should be quite easy to parallelize one simple loop. See Cheeso post about ThreadPools workers. – Elalfer Apr 18 '11 at 18:39
  • Added a plinq implementation to parallelize the loop – Brook Apr 18 '11 at 18:51
  • @softplaza, as an argument against multithreading that you may want to consider, read the accepted answer here and see if any or all of it pertains to you. http://stackoverflow.com/questions/5686605/whats-a-good-architecture-for-a-life-simulator/5687220#5687220 – Anthony Pegram Apr 18 '11 at 19:10
  • If all you want is an equal opportunity to go first, then a shuffle followed by sequential processing is likely sufficient. If you want processing to happen more or less simultaneously and are willing to work through all the challenges that could present, then maybe you consider multithreading. – Anthony Pegram Apr 18 '11 at 19:11
  • Ok I think i'm done updating now :) Added the "shuffle" method, as well as the parallel/plinq way. – Brook Apr 18 '11 at 19:19
4

ThreadPool.QueueUserWorkItem

Example from the documentation page:

using System;
using System.Threading;
public class Example {
    public static void Main() {
        // Queue the task.
        ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc));

        Console.WriteLine("Main thread does some work, then sleeps.");

        // If you comment out the Sleep, the main thread exits, and
        // therefore the application ends, before the work item 
        // runs on the thread pool thread.  The thread pool uses background
        // threads, which do not keep the application running.  (This
        // is a simple example of a race condition.)
        Thread.Sleep(1000);

        Console.WriteLine("Main thread exits.");
    }

    // This method performs the work. It will be invoked in a thread
    // running in the .NET threadpool.
    static void ThreadProc(Object stateInfo) {
        // No state object was passed to QueueUserWorkItem, so 
        // stateInfo is null.
        Console.WriteLine("Hello from the thread pool.");
    }
}

In your scenario you could simply queue all the workitems from the various lists. There is no guarantee of order-of-processing with the threadpool. However, if the work to be done is fairly "short" - that is to say if the time required for thread dispatch is "about the same as" the time required performing actual work - then you may want to shuffle the actions, so as to provide a fairer chance among all the threadpool threads.

If your actions run for a longish time - let's say tens of seconds or more - then the threadpool will provide good fairness anyway, and assuming no contention among the threads, you probably won't need to shuffle the workitems before queuing them.

Cheeso
  • 189,189
  • 101
  • 473
  • 713
  • can you give me an example? for ThreadPool.QueueUserWorkItem and my application? i haven't used it before so need help with it thats a lot –  Apr 18 '11 at 18:45
  • sure, see above. This is directly from the documentation page that I linked to originally. – Cheeso Apr 18 '11 at 18:53
  • Threadpool will work just fine, the primary difference with using ThreadPool and something like plinq is that it's not going to block until the work is done unless you implement and maintain wait handles then call `WaitHandle.WaitAll(events)`. I don't know if that matters for your implementation or not. – Brook Apr 18 '11 at 19:00
1

You can run all of them in parallel using multiple threads (Cheeso's answer) - note that there are absolutely no guarantees that all methods will run at the same time in this case. You should define what "equal chance" actually means in your case befor jumping into multithreaded approach.

Or you need to decide what order you want you function to be executed and make appropriate ordering. I.e. assign priorities put all of the in the same list (see Brook's answer) and sort them by priority.

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179