Не удается получить результаты TaskCompletionSource
Здравствуйте, у меня есть следующая проблема:
Я хочу выполнить что-то похожее на транзакцию. Я хочу выполнить ряд async
операции после получения внешнего триггера. Поэтому я использую TaskCompletionSource
это устанавливается в методе, представляющем триггер:TriggerTransaction
,
Этот триггерный метод вызывается в Main
в пуле потоков, когда я нажимаю определенную консольную клавишу.
После того, как я нажимаю A
Ключевое слово TriggerTransaction
исполняется и TaskCompletionSource
-s получить set.Still основной поток не вычисляет сумму двух ожидаемых задач.
class Program
{
public static Task<Task<int>> TransactionOperation1()
{
TaskCompletionSource<Task<int>> tcs = new TaskCompletionSource<Task<int>>();
tasks.Add(tcs);
Task<Task<int>> result = tcs.Task;
return result;
}
public static Task<Task<int>> TransactionOperation2()
{
TaskCompletionSource<Task<int>> tcs = new TaskCompletionSource<Task<int>>();
tasks.Add(tcs);
Task<Task<int>> result = tcs.Task;
return result;
}
public static async Task<int> ExecuteTransactionOnDB()
{
await Task.Delay(1000);
return 5;
}
public static async Task TriggerTransaction()
{
int value = await ExecuteTransactionOnDB();
foreach (var item in tasks)
{
item.SetResult(value);
}
}
public static List<dynamic> tasks = new List<dynamic>();
static async Task Main(string[] args)
{
Task<Task<int>> a = TransactionOperation1();
Task<Task<int>> b = TransactionOperation2();
Task.Run(async() =>
{
while (Console.ReadKey().Key != ConsoleKey.A) ;
await TriggerTransaction();
});
if (!File.Exists("D:\\data.txt"))
{
File.Create("D:\\data.txt");
}
using(FileStream stream=new FileStream("data.txt",FileMode.Append,FileAccess.Write))
{
int sum=await await a + await await b;//thread wont pass this line when tasks are set.
ReadOnlyMemory<byte> bytes = Encoding.UTF8.GetBytes(sum);
stream.Write(bytes.ToArray());
}
Console.WriteLine(await await a + await await b);
}
}
}
PS Если вам интересно, почему я использовал List<dynamic>
хранить TaskCompletionSource
-s, это потому, что TransactionOperations будут отличаться по типу возврата. Некоторые из них вернутся int
, другие String
..Bool
..так далее.
Для лучшего понимания я сделал схему. Как вы увидите, есть:
-Список, где я хочу хранить TCS-ы
-Некоторые вызовы, которые завершаются только после того, как был установлен внешний триггер (транзакция была выполнена)
Как вы можете видеть в Calls
, все имеют разные типы возврата.
2 ответа
Зачем вам нужен Task<Task<int>>
? Просто Task<int>
достаточно, и соответственно, TaskCompletionSource<int>
, И вы также избавляетесь от неловкого await await ...
, что не требуется в вашем случае.
Обратите внимание, что я также добавил Close()
в поток, возвращенный File.Create()
,
Вот рабочая версия программы:
class Program
{
public static Task<int> TransactionOperation1()
{
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
tasks.Add(tcs);
return tcs.Task;
}
public static Task<int> TransactionOperation2()
{
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
tasks.Add(tcs);
return tcs.Task;
}
public static async Task<int> ExecuteTransactionOnDB()
{
await Task.Delay(1000);
return 5;
}
public static async Task TriggerTransaction()
{
int value = await ExecuteTransactionOnDB();
foreach (var item in tasks)
{
item.SetResult(value);
}
}
public static List<dynamic> tasks = new List<dynamic>();
static async Task Main(string[] args)
{
Task<int> a = TransactionOperation1();
Task<int> b = TransactionOperation2();
Task input = Task.Run(async () => {
while (Console.ReadKey().Key != ConsoleKey.A);
await TriggerTransaction();
});
if (!File.Exists("C:\\temp\\data.txt"))
{
File.Create("C:\\temp\\data.txt").Close();
}
using (FileStream stream = new FileStream("C:\\temp\\data.txt", FileMode.Append, FileAccess.Write))
{
int sum = await a + await b; // now it works ok
var bytes = Encoding.UTF8.GetBytes(sum.ToString());
stream.Write(bytes);
}
Console.WriteLine(await a + await b);
}
}
Проверьте измененную версию кода, он даст ожидаемый результат, выполнив Task
создан с использованием TaskCompletionSource
, Я также сделал код Generic, так что вам не нужно использовать dynamic
введите и определите тип данных во время компиляции
static async Task Main(string[] args)
{
var a = Program<int>.TransactionOperation1();
var b = Program<int>.TransactionOperation2();
await Task.Run(async() =>
{
Console.ReadLine();
await Program<int>.TriggerTransaction(5);
});
if (!File.Exists("D:\\data.txt"))
{
File.Create("D:\\data.txt");
}
using (FileStream stream = new FileStream("D:\\data.txt", FileMode.Append, FileAccess.Write))
{
int sum = await a + await b;//thread wont pass this line when tasks are set.
var bytes = Encoding.UTF8.GetBytes(sum.ToString());
stream.Write(bytes, 0, bytes.Length);
}
Console.WriteLine(await a + await b);
}
class Program<T>
{
public static Task<T> TransactionOperation1()
{
var tcs = new TaskCompletionSource<T>();
tasks.Add(tcs);
return tcs.Task;
}
public static Task<T> TransactionOperation2()
{
var tcs = new TaskCompletionSource<T>();
tasks.Add(tcs);
return tcs.Task;
}
public static async Task<T> ExecuteTransactionOnDB(T t)
{
return await Task.FromResult(t);
}
public static async Task TriggerTransaction(T t)
{
T value = await ExecuteTransactionOnDB(t);
foreach (var item in tasks)
{
item.SetResult(value);
}
}
public static List<TaskCompletionSource<T>> tasks = new List<TaskCompletionSource<T>>();
}
Ниже приведены важные модификации:
List<dynamic>
заменяетсяList<TaskCompletionSource<T>>
TransactionOperation1/2
иметь тип возвратаTask<T>
, которая является задачей, созданной с использованиемTaskCompletionSource<T>
Добавлено дополнительное ожидание
Task.Run
, который выполняетTriggerTransaction
внутренне, хотя вы можете заменить следующий код:await Task.Run(async() => { Console.ReadLine(); await Program<int>.TriggerTransaction(5); });
с
await Program<int>.TriggerTransaction(5);
Теперь он выдает результат, как вы ожидаете, и суммирует два целых числа. Еще несколько небольших изменений, таких как удаление Task.Delay
, который не требуется
РЕДАКТИРОВАТЬ 1 - Использование Task.WhenAll
static async Task Main(string[] args)
{
var a = Program.TransactionOperation1(5);
var b = Program.TransactionOperation1(5);
Console.ReadLine();
var taskResults = await Task.WhenAll(a,b);
dynamic finalResult = 0;
foreach(var t in taskResults)
finalResult += t;
if (!File.Exists("D:\\data.txt"))
{
File.Create("D:\\data.txt");
}
using (FileStream stream = new FileStream("D:\\data.txt", FileMode.Append, FileAccess.Write))
{
var bytes = Encoding.UTF8.GetBytes(finalResult.ToString());
stream.Write(bytes, 0, bytes.Length);
}
Console.WriteLine(finalResult);
}
class Program
{
public static Task<dynamic> TransactionOperation1(dynamic val)
{
return Task<dynamic>.Run(() => val);
}
public static Task<dynamic> TransactionOperation2(dynamic val)
{
return Task<dynamic>.Run(() => val);
}
}