0

I have a rather large class which contains plenty of fields (10+), a huge array (100kb) and some unmanaged resources. Let me explain by example

class ResourceIntensiveClass
{
    private object unmaganedResource; //let it be the expensive resource
    private byte[] buffer = new byte[1024 * 100]; //let it be the huge managed memory
    private Action<ResourceIntensiveClass> OnComplete;


    private void DoWork(object state)
    {
        //do long running task
        OnComplete(this); //notify callee that task completed so it can reuse same object for another task
    }

    public void Start(object dataRequiredForCurrentTask)
    {
        ThreadPool.QueueUserWorkItem(DoWork); //initiate long running work
    }
}

The problem is that the start method never returns after the 10000th iteration causing a stack overflow. I could execute the OnComplete delegate in another thread giving a chance for the Start method to return, but it requires using extra cpu time and resources as you know. So what is the best option for me?

b4hand
  • 9,550
  • 4
  • 44
  • 49
TakeMeAsAGuest
  • 957
  • 6
  • 11

6 Answers6

3

Is there a good reason for doing your calculations recursively? This seems like a simple loop would do the trick, thus obviating the need for incredibly deep stacks. This design seems especially problematic as you are relying on main() to setup your recursion.

dlev
  • 48,024
  • 5
  • 125
  • 132
  • yes, Start initiates other methods on threadpool, actually oncomplete is invoked on another thread. the provided code is just a illustration of what i am currently doing not the real situation. – TakeMeAsAGuest Apr 08 '11 at 19:24
  • You might need to give more specifics, then, because the code above probably shouldn't be written the way it is. More details would help. – dlev Apr 08 '11 at 19:30
  • thanks but the class is written the way it is. it accomplishes long running tasks using both unmanaged expensive resources and managed big memory. the problem here is not my code, the situation – TakeMeAsAGuest Apr 08 '11 at 19:33
  • "the problem here is not my code, the situation" - what dlev and others are saying here is that, based on your example, perhaps the solution you've coded is not a good match for the situation – matt b Apr 08 '11 at 20:03
  • i think the situation is crystal clear. i set delegate to get notified upon completion to proceed to the next task. assume task progression is linear. – TakeMeAsAGuest Apr 08 '11 at 20:14
1

recursive methods can get out of hand quite fast. Have you looked into using Parallel Linq? you could do something like

(your Array).AsParallel().ForAll(item => item.CallMethod());

you could also look into the Task Parallel Library (TPL)

with tasks, you can define an action and a continue with task.

The Reactive Framework (RX) on the other hand could handle these on complete events in an async manner.

Alexandre Brisebois
  • 6,599
  • 12
  • 50
  • 69
1

Where are you changing the value of taskData so that its length can ever equal currentTaskIndex? Since the tasks you are assigning to the data are never changing, they are being carried out forever...

Alex
  • 4,844
  • 7
  • 44
  • 58
  • do i copy my whole project? using threads, understanding recursive nature of program flow, using delegates etc but making a design just for 2 objects. hmm so enlightening answer. – TakeMeAsAGuest Apr 08 '11 at 21:16
  • It depends what you're trying to do... Obvious choice would be to remove a task from the array and make sure that `currentTaskIndex' == 0 when it's finished. Then you can return the method and jump out of recursion. Alternatively you could use a 'for' loop to do it which would be simpler ;) – Alex Apr 08 '11 at 21:20
  • 1-in actual code there isnt currentTaskIndex.2-then i can return to where? to the air? DoWork is executing in threadpool. it just returns to the threadpool. – TakeMeAsAGuest Apr 08 '11 at 21:25
  • Check out this page on Threading in C#... apologies if it's too basic for you, I'm more familiar with Java. http://www.suite101.com/article.cfm/c_sharp/96436 – Alex Apr 08 '11 at 21:29
  • thanks but ive got nothing to do with thread abort, suspend, interrupt. by the way, they should be used as a last resort and in almost all situations they should be and "can be" avoided using other technics. – TakeMeAsAGuest Apr 08 '11 at 21:34
0

I would guess that the problem arises from using the pre-increment operator here:

 if(c.CurrentCount < 10000)
    c.Start(++c.CurrentCount);

I am not sure of the semantics of pre-increment in C#, perhaps the value passed to a method call is not what you expect.

But since your Start(int) method assigns the value of the input to this.CurrentCount as it's first step anyway, you should be safe replacing this with:

 if(c.CurrentCount < 10000)
    c.Start(c.CurrentCount + 1);

There is no point in assigning to c.CurrentCount twice.

matt b
  • 138,234
  • 66
  • 282
  • 345
  • as i mentioned before, that code is completely dummy to illustrate the situation. in actual scenario, start initiates other methods on another threads. and after that methods finished, the class is free for other waiting tasks untill all complete. – TakeMeAsAGuest Apr 08 '11 at 19:41
  • I'm not sure which part of my question you are addressing? My suggestion is to use `foo + 1` instead of `++foo`. Or are you stating that the real code does not do `start(++foo)`? If so, you may want to re-work your code sample to be closer to the real thing. – matt b Apr 08 '11 at 19:42
  • I'm afraid that doesn't help, as this new sample doesn't show the recursion - do the listeners of `OnComplete()` invoke `DoWork()` again? – matt b Apr 08 '11 at 20:04
  • wouldn't `ThreadPool.QueueUserWorkItem(DoWork)` return after queueing the work item? This isn't even really recursion then if so. – matt b Apr 08 '11 at 20:21
  • yes it return immediately. but say subscriber method say OnCompleteHandler method, Start will be called again. – TakeMeAsAGuest Apr 08 '11 at 20:25
0

If using the threadpool, I assume you are protecting the counters (c.CurrentCount), otherwise concurrent increments will cause more activity, not just 10000 executions.

Haplo
  • 1,368
  • 9
  • 18
0

There's a neat tool called a ManualResetEvent that could simplify life for you.

Place a ManualResetEvent in your class and add a public OnComplete event.

When you declare your class, you can wire up the OnComplete event to some spot in your code or not wire it up and ignore it.

This would help your custom class to have more correct form.

When your long process is complete (I'm guessing this is in a thread), simply call the Set method of the ManualResetEvent.

As for running your long method, it should be in a thread that uses the ManualResetEvent in a way similar to below:

private void DoWork(object state)
{
    ManualResetEvent mre = new ManualResetEvent(false);
    Thread thread1 = new Thread(
      () => {
      //do long running task
      mre.Set();
    );
    thread1.IsBackground = true;
    thread1.Name = "Screen Capture";
    thread1.Start();
    mre.WaitOne();
    OnComplete(this); //notify callee that task completed so it can reuse same object for another task
}
  • it requires creating thread per task + switching to kernel mode + blocking unneccesarly. to be more clearer, the long running task uses io completion port, which doesnt neccessarly need a thread per task. – TakeMeAsAGuest Apr 08 '11 at 21:28