views:

261

answers:

1

Hello everybody, hope you're well.

I'm facing a curious problem with BeginInvoke and I really really need your help !

I have a class Reporting which contains multiple instances of type Report

Class Reporting : UserControl
{
  //Reports are inherited from UserControl
  Report _report1;
  Report _report2;
  Report _report3;

  //Instanciate and return the Report corresponding to the passed ID (The report is 
  //instanciated and created only if it's null)   
  public Report GetReport(int reportId);

  public delegate void GenerateReportAsImageDelegate(int reportId,string path)

  //Generate the report and save it as image
  public void GenerateReportAsImage(int reportId,string path)
  {
    if(InvokeRequired)
    {
      BeginInvoke(new GenerateReportAsImageDelegate(GenerateReportAsImage),new      object[]{reportId,path});

    }
    else
    {
      //... Generating the report etc..
    }
 }

  ....
}

It's a user control shown in a form and this same usercontrol is also used from a Windows Service to generate reports (and saved as image) every minutes.

To generate a report every minute, i'm using the System.Threading.Timer.

Here is what my class generating the reports looks like in the service :

class ReportServiceClass
{

  Reporting _reportingObject;
  System.Threading.Timer _timer;

  Public ReportingServiceClass(int reportId)
  {
     _timer = new Timer(new TimerCallback(this.CreateReport), reportId, 0, 1000) 
  }

  private void CreateReport(Object stateInfo)
  {  
     int reportId = Convert.ToInt32(stateInfo);

    //To ensure that the _reportingObject is created on the same thread as the report
    if(_reportingObject == null)
      _reportingObject = new _reportingObject();

    _reportingObject.GenerateReportAsImage(reportId,@"c:\reports\report.PNG")
  }

}

Almost everything is working well .. excepet that sometimes CreateReport is executed in an other thread of the ThreadPool. So when i'm performing some actions on the report and its components (which have been created in an other thread), the InvokeRequired is set to true and it's totaly obvious... But the BeginInvoke does not perform any action ! It's almost like the thread where the report have been created no longer exist ...

You guys have any ideas on how to avoid this problem ?

It's been now a week that i'm facing this problem, i haved googled and stackoverflowed . but nothing !

Thanks a lot !

A: 

The following is from the ThreadPool.SetMinThreads documentation:

Idle threads are maintained by the thread pool in order to reduce the time required to satisfy requests for thread pool threads. Separate minimums are maintained for worker threads and asynchronous I/O threads. Idle threads in excess of the minimums are terminated, to save system resources. Maintenance of the idle threads is a background task.

Also, my understanding is that BeginInvoke and Invoke rely on the WinForms "message pump" for the actual invocation of your code. I have never heard of them being used in a service - it seems like the mechanism is designed for a real WinForms application with a visible UI and a message pump. Getting it to work reliably in a service may be possible - but it seems like you should at least create your own thread rather than using the ThreadPool for the thread which creates the UserControl and manages the invocations.

You may or may not want to use Invoke rather than BeginInvoke. The former is synchronous and the latter is asynchronous.

Joe Erickson
I'm not using explicitly the ThreadPool, but My TimerCallBack is been launched in the ThreadPool. I don't know on what problem i have to focus :- Do i have to make sure that my TimerCallBack is always launched in the same threador- Do i have to not use BeginInvoke (as this mechanism is designed for a real Windows Form) and to look for an other way to perfom tasks on my report when CreateReport is executed on a different thread than the one where the report has been created.
Pato