Я хотел бы знать, можно ли убрать ожидание из этого метода, чтобы не было асинхронного метода.
public static async Task Run(IMinioClient minio,
string bucketName = "my-bucket-name",
string prefix = null,
bool recursive = true,
bool versions = false)
{
try
{
Console.WriteLine("Running example for API: ListObjectsAsync");
var listArgs = new ListObjectsArgs()
.WithBucket(bucketName)
.WithPrefix(prefix)
.WithRecursive(recursive)
.WithVersions(versions);
await foreach (var item in minio.ListObjectsEnumAsync(listArgs).ConfigureAwait(false))
Console.WriteLine($"Object: {item.Key}");
Console.WriteLine($"Listed all objects in bucket {bucketName}\n");
}
catch (Exception e)
{
Console.WriteLine($"[Bucket] Exception: {e}");
}
}
https://github.com/minio/minio-dotnet/blob/master/Minio.Examples/Cases/ListObjects.cs
Спасибо за помощь.
Обычно для этого я использую .Result, но здесь ожидание выполняется по foreach.
зачем вам использовать асинхронный метод, такой как ListObjectsEnumAsync
, если вы не хотите выполнять его асинхронно? Чего именно вы хотите здесь добиться? Что ваша функция должна делать со списком?
вам нужно переосмыслить это; вот соответствующее обсуждение - речь идет не конкретно о .NET, а об асинхронных и неасинхронных методах (тема, которая влияет на многие среды выполнения/языки) - Journal.stuffwithstuff.com/2015/02/01/… - асинхронный код принципиально отличается, и вы не можете просто отмахнуться от него и представить его неасинхронным способом.
Клиент Minio, предположительно, как и любой S3-совместимый клиент для .Net, который вы могли бы использовать вместо него, написан на асинхронном уровне. Зачем бороться с этим, а не просто использовать? Попытка использовать асинхронные вещи способом синхронизации часто приводит к трудно выявляемым проблемам.
Также вы связали источник, просто посмотрите предыдущую регистрацию этого файла/метода в git. В его подписи не было Task. Возможно, эта версия все еще работает с неизмененным в остальном клиентом minio.
Проблема XY?
Вы можете использовать ToEnumerable()
или ToBlockingEnumerable()
для преобразования асинхронного перечислимого в перечисляемое. Смотрите этот ответ.
Довольно странно просто не использовать асинхронное выполнение, когда это возможно, но я оставлю это на ваше усмотрение. Просто помните о рисках взаимоблокировок и снижения производительности.
Если вам действительно не нужно использовать await
, вот реализация без него. Немного многословно и не так чисто, как с await
, но вот оно.
Во-первых, давайте определим пример метода, возвращающего IAsyncEnumerable
:
public static class TestClass
{
public static async IAsyncEnumerable<int> GetIntsAsync()
{
for (int i = 0; i < 10; i++)
{
await Task.Delay(100);
yield return i;
}
}
}
и реализация без await
будет:
var asyncEnumerator = TestClass.GetIntsAsync().GetAsyncEnumerator();
try
{
while (asyncEnumerator.MoveNextAsync().AsTask().GetAwaiter().GetResult())
{
Console.WriteLine(asyncEnumerator.Current);
}
}
finally
{
asyncEnumerator?.DisposeAsync().AsTask().GetAwaiter().GetResult();
}
Для удобства вы можете определить методы расширения для таких сценариев:
public static class Extensions
{
public static T AwaitSynchornously<T>(this ValueTask<T> valueTask)
{
return valueTask.AsTask().GetAwaiter().GetResult();
}
public static void AwaitSynchornously(this ValueTask valueTask)
{
valueTask.AsTask().GetAwaiter().GetResult();
}
public static T[] LoopSynchronously<T>(
this IAsyncEnumerable<T> asyncEnumerable,
Action<T> action)
{
return asyncEnumerable.EnumerateSynchronously(action).ToArray();
}
public static IEnumerable<T> EnumerateSynchronously<T>(
this IAsyncEnumerable<T> asyncEnumerable,
Action<T> action)
{
var asyncEnumerator = asyncEnumerable.GetAsyncEnumerator();
try
{
while (asyncEnumerator.MoveNextAsync().AwaitSynchornously())
{
action(asyncEnumerator.Current);
yield return asyncEnumerator.Current;
}
}
finally
{
asyncEnumerator?.DisposeAsync().AwaitSynchornously();
}
}
}
Использование тогда очень просто:
TestClass.GetIntsAsync().LoopSynchronously(Console.WriteLine);
и использование valueTask.AsTask()
в цикле, даже если это не приведет к тупику, приведет к чрезмерному выделению GC.
@Дай, как думаешь, можно обойтись без AsTask()
?
Я бы просто сделал один метод расширения для IAsyncEnumerable
без Action
@Dai Ваш комментарий действителен, но ОП специально просил такое решение (альтернативой является использование Result
, но оно имеет те же риски). Вот почему я упомянул, что это странный поступок... Я обновлю свой ответ вашим предложением, хотя думаю, что ОП осознает риск, на который он идет. Более того, невозможно синхронно ждать ValueTask
без AsTask
— опять же, это необходимо для выполнения того, что нужно OP. Я оставляю внимание ему
@IvanPetrov Это был всего лишь пример, я оставлю ОП решать, нужно ли ему использовать Action
или, может быть, Func
, а может быть, вообще ничего :)
@MichałTurczyn почти искушает меня опубликовать ответ, который я написал, прежде чем увидеть ваш :)
Вы можете использовать API .NET 7 ToBlockingEnumerable:
Преобразует экземпляр
IAsyncEnumerable<T>
в экземплярIEnumerable<T>
, который перечисляет элементы блочным способом.
Пример использования:
public static void Run(IMinioClient minio,
string bucketName = "my-bucket-name",
string prefix = null,
bool recursive = true,
bool versions = false)
{
var listArgs = new ListObjectsArgs()
.WithBucket(bucketName)
.WithPrefix(prefix)
.WithRecursive(recursive)
.WithVersions(versions);
foreach (var item in minio.ListObjectsEnumAsync(listArgs).ToBlockingEnumerable())
{
Console.WriteLine($"Object: {item.Key}");
}
}
Это решение подвержено взаимоблокировкам в зависимости от реализации метода ListObjectsEnumAsync
. Блокировка по асинхронному коду вообще нецелесообразна.
Вы уверены, что это конкретное решение подвержено взаимоблокировкам - в документации ничего не упоминается, и мы фактически «ждем», используя ту же инфраструктуру с частным ManualResetEventWithAwaiterSupport
классом
@IvanPetrov да, я почти уверен. Если ListObjectsEnumAsync
реализован с помощью async
/await
, и есть хотя бы один await
без ConfigureAwait(false)
, и код вызывается в потоке пользовательского интерфейса приложения WinForms или WPF, взаимоблокировка почти гарантирована.
Вы звоните
ListObjectsEnumAsync
. Существует ли также методListObjectsEnum
, который возвращаетIEnumerable
вместоIAsyncEnumerable
?