Reputation: 7783
I want to use this Task<TResult>
constructor. I can't seem to get the syntax right. Could someone correct my code?
Also, am I right thinking that if a Task
is constructed that way, it's not started?
The constructor I think I need is:
Task<TResult>(Func<Object, TResult>, Object)
The error I get is:
Argument 1: cannot convert from 'method group' to '
System.Func<object,int>
'
static void Main(string[] args)
{
var t = new Task<int>(GetIntAsync, "3"); // error is on this line
// ...
}
static async Task<int> GetIntAsync(string callerThreadId)
{
// ...
return someInt;
}
Upvotes: 83
Views: 70040
Reputation: 43384
To use the Task
constructor that accepts an object state
argument you must have a function that accepts an object
argument too. Generally this is not convenient. The reason that this constructor exists is for avoiding the allocation of an object (a closure) in hot paths. For normal usage the overhead of closures is negligible, and avoiding them will complicate your code for no reason. So this is the constructor you should use instead:
public Task (Func<TResult> function);
...with this lambda as argument:
() => GetIntAsync("3")
There is one peculiarity in your case though: the lambda you pass to the constructor returns a Task<int>
. This means that the generic type TResult
is resolved to Task<int>
, and so you end up with a nested task:
var t = new Task<Task<int>>(() => GetIntAsync("3"));
Starting the outer task will result to the creation of the inner task. To get the final result you'll have to use the await
operator twice, one for the completion of the outer task, and one for the completion of the inner task:
static async Task Main(string[] args)
{
var outerTask = new Task<Task<int>>(() => GetIntAsync("3"));
//...
outerTask.Start(TaskScheduler.Default); // Run on the ThreadPool
//outerTask.RunSynchronously(TaskScheduler.Default) // Run on the current thread
//...
Task<int> innerTask = await outerTask; // At this point the inner task has been created
int result = await innerTask; // At this point the inner task has been completed
}
Upvotes: 14
Reputation: 6317
Had a similar issue where we wanted to ensure a task was re-usable without re-starting the task. Rx saved our bacon. You'll need the nuget package System.Reactive
.
See below GetIntTask
returns a Task that isn't started, and GetIntOnce
ensures the task gets started, and the result of the task is buffered and replayed to any callers (with Replay(1)
)
async Task Main()
{
var awaitableResult = GetIntOnce();
Console.WriteLine("Has the task started yet? No!");
var firstResult = await awaitableResult;
Console.WriteLine($"Awaited Once: {firstResult}");
var secondResult = await awaitableResult;
Console.WriteLine($"Awaited Twice: {secondResult}");
}
public static IObservable<int> GetIntOnce()
{
return Observable.FromAsync(() =>
{
var t = GetIntTask();
t.Start();
return t;
}).Replay(1).AutoConnect();
}
public static Task<int> GetIntTask()
{
var t = new Task<int>(
() =>
{
Console.WriteLine("Task is running!");
return 10;
});
return t;
}
The output is:
Has the task started yet? No!
Task is running!
Awaited Once: 10
Awaited Twice: 10
Upvotes: 2
Reputation: 2524
A new approach in .net 5 (Actually I have no idea when you can start doing this, I'm using .net 5 now)
Task<int> MyStartLaterTask = null;
//... Some Time Later
MyStartLaterTask ??= GetIntAsync(string callerThreadId);
await MyStartLaterTask;
I appreciate that this isn't exactly "define upfront and use later" but as far as I can see this does the same thing. I have a loop and I wanted to define a task that was only run when needed and only run once. This does just that, it creates a task that I can assign when it's first needed and then await it whenever its output is required.
Upvotes: -4
Reputation: 34947
I find that an async lambda works best, because:
.Start
, so it's not possible to forget to do it.Func<Task<int>> f = async () => await WorkAsync(2);
Console.WriteLine("Sleeping before start");
await Task.Delay(100);
Console.WriteLine("Waking up to start");
var result = await f();
Console.WriteLine($"The work is done and the answer is: {result}");
This results in the following output:
Sleeping before start
Waking up to start
Starting 2
Ending 2
The work is done and the answer is: 4
Upvotes: 4
Reputation: 4742
var t = new Task<int>(() => GetIntAsync("3").Result);
Or
var t = new Task<int>((ob) => GetIntAsync((string) ob).Result, "3");
To avoid using lambda, you need to write a static method like this:
private static int GetInt(object state)
{
return GetIntAsync(((string) state)).Result;
}
And then:
var t = new Task<int>(GetInt, "3");
Upvotes: 59
Reputation: 126
//creating task
var yourTask = Task<int>.Factory.StartNew(() => GetIntAsync("3").Result);
//...
int result = yourTask.Result;
UPDATE:
Yes, unfortunately it does start the task. Use code as mentioned above instead:
//creating task
var yourTask = new Task<int>(() => GetIntAsync("3").Result);
//...
// call task when you want
int result = yourTask.Start();
Upvotes: -6