Я хотел бы знать, можно ли убрать ожидание из этого метода, чтобы не было асинхронного метода.
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?