Fredy
Fredy

Reputation: 251

How to invoke a method on specific running task

I'm new in the themes tasks.

How to get access to the right task and invoke the method on this task? The task has an ID and I gave a Name to it and I got a TaskScheduler.

I get the Exception, which is correct:

InvalidOperationException: The calling thread can not access this object because the object is owned by another thread.

In the Main I start a Task with CustomSplashScreenViewModel.StartSplashScreenAsync(). More Tasks get startet and are running. With the Event when everything has loaded I need to Close my SplashScreen.

The method CustomSplashScreenViewModel.CloseSplash(_splashTask, _taskSchedulerSplash); gets that information. When I debug it, the _splashTask is "null" and in the _taskSchedulerSplash it has the _splashTask inside.

Inside the CloseSplash Method I like to invoke the method _view.Close() on the _splashTask.

How do I solve this?

public class Program
{
  private static readonly SingleThreadTaskScheduler _taskSchedulerSplash = new SingleThreadTaskScheduler(ApartmentState.STA);

  [ThreadStatic]
  private static Task _splashTask;

//        [STAThread]
  public static async Task Main()
  {       
    _splashTask = await Task.Factory.StartNew(() =>
    {
        return CustomSplashScreenViewModel.StartSplashScreenAsync();
    }, _taskSchedulerSplash);
    
    var taskScheduler = new SingleThreadTaskScheduler(ApartmentState.STA);
    var taskList = new List<Task>(); 

    var updateTask = Task.Run(InstallHelper.CheckForUpdatesAsync);
    updateTask.Wait();
    taskList.Add(updateTask);

    var tasks = await Task.Factory.ContinueWhenAll(taskList.ToArray(), result => Task.Factory.StartNew( ()=>AppStart(container), taskScheduler));
        tasks.Wait();
   }
 

   private static void App_MainWindowLoaded(object sender, EventArgs e)
   {
     CustomSplashScreenViewModel.CloseSplash(_splashTask, _taskSchedulerSplash);
   }
}

public class CustomSplashScreenViewModel
{
  private static Thread _currentThread;
  public static void CloseSplash(Task task, TaskScheduler taskScheduler)
  {
    if (_view!= null)
    {
      // Here i need the right Task and to invoke the following method
      // if I'm doing _view.Dispatcher.Invoke(new Action(_view.Close)); it doesn't 
      // work coz it's another STA Thread. Inside this class I can store the right task in 
      // a local variable. this works. But how to invoke it if the Task is already running?
      // I like to overgive the task - may it isn't possible. 
      _view.Close();      
    }
  }
}

Upvotes: 0

Views: 586

Answers (1)

Stephen Cleary
Stephen Cleary

Reputation: 457217

You can't change a running task. However, you can schedule another task that will run on the same scheduler, which should work just fine for what you need (it'll run on the same STA thread that owns the splash screen):

public static void CloseSplash(Task task, TaskScheduler taskScheduler)
{
  if (_view!= null)
  {
    Task.Factory.StartNew(() => _view.Close(), taskScheduler);
  }
}

On a side note, I'd recommend these to make the code easier to understand:

  1. Introducing a TaskFactory instance for your custom task scheduler instead of using TaskScheduler with Task.Factory.
  2. Avoid Task.Factory.ContinueWhenAll and Task.Wait; use Task.WhenAll and await instead.

Upvotes: 1

Related Questions