2015-03-12 14 views
7

Ponieważ muszę wykonać wiele operacji we/wy plików w mojej aplikacji, Postanowiłem zaimplementować je asynchronicznie. Patrząc na MSDN, nie ma asynchronicznych odpowiedników dla File.Create, File.Delete i File.Move. Jak dowiedziałem się, powodem jest nieistnienie asynchronicznego realizacji Win32 dla pliku usuwać, tworzyć lub przenieść, więc skończyło się następujące rozwiązanie:Jak zaimplementować plik async.Delete/Create/Move?

public static Task DeleteAsync(string path) 
{ 
    Guard.FileExists(path); 

    return Task.Run(() => File.Delete(path)); 
} 

public static Task<FileStream> CreateAsync(string path) 
{ 
    Guard.IsNotNullOrWhitespace(path); 

    return Task.Run(() => File.Create(path)); 
} 

public static Task MoveAsync(string sourceFileName, string destFileName) 
{ 
    Guard.FileExists(sourceFileName); 
    Guard.IsNotNullOrWhitespace(destFileName); 

    return Task.Run(() => { File.Move(sourceFileName, destFileName); }); 
} 

Uwzględniając Paradigma "Don’t use Task.Run in Libraries", zastanawiam się, czy istnieje lepsza implementacja czy powinienem zrezygnować z kodu synchronicznego?

Wielkie dzięki z góry!

Edits:


  • Poprawiono kod na podstawie Peter Duniho zalecenia
  • dodał link do oryginalnego blogu dostarczonych przez Sriram Sakthivel
+0

Kto mówi "nie używaj' Task.Run() 'w bibliotekach"? Jak myślisz, w jaki sposób możesz wykonywać synchroniczne metody bez użycia tego lub czegoś podobnego? Czy jest coś nie w porządku z wdrożeniem, które masz? –

+1

@PeterDuniho Rekomendacja Stephen cleary. On zaleca, aby nie używać 'Task.Run' w implementacji, jeśli chcesz zawinąć synchroniczną metodę jako operację asynchroniczną, zrób to w kodzie klienta tam, gdzie jest to potrzebne.Edycja: Stephen Toub mówi również, że nie naraża asynchronicznego opakowania na metody synchroniczne. –

+0

Nawiasem mówiąc, twoje implementacje wydają się mniej niż doskonałe. Wszystkie metody powinny po prostu zwrócić 'Task' (bez konfiguracji oczekującej i bez metod' async'). 'CreateAsync()' może 'zwrócić Task.Run (() => File.Create (path));' (tzn. Zwrócone zadanie zwróciło samo obiekt 'FileStream' ... nie ma potrzeby czekania na siebie, aby to zrobić ani używać przechwytywania zmiennych w celu jego wykonania). –

Odpowiedz

6

Jeśli musisz to zrobić, Napisałbym takie metody (uwaga: chętnie się zgadzam, że to właśnie Stephens Cleary i Toub namawiają nas, abyśmy tego nie robili):

public static Task DeleteAsync(string path) 
{ 
    Guard.FileExists(path); 

    return Task.Run(() => { File.Delete(path); }); 
} 

public static Task<FileStream> CreateAsync(string path) 
{ 
    Guard.IsNotNullOrWhitespace(path); 

    return Task.Run(() => File.Create(path)); 
} 

public static Task MoveAsync(string sourceFileName, string destFileName) 
{ 
    Guard.FileExists(sourceFileName); 
    Guard.IsNotNullOrWhitespace(destFileName); 

    return Task.Run(() => { File.Move(sourceFileName, destFileName); }); 
} 

Spowoduje to wyczyszczenie kodu i wyeliminowanie nadmiernego kontekstu/przełączania wątków.

W kontekście programu opartego na GUI, wydaje się, że dobrze jest używać takich wrapperów. Myślę, że dopóki nie stworzysz zupełnie nowej biblioteki z synchronicznymi i asynchronicznymi interfejsami API równolegle, jak to opisano w artykułach, o których mowa, nie jest to straszne.

Jednak dla mnie, większym problemem jest to, że żadna z tych operacji nie potrwa wystarczająco długo, aby uzasadnić ich asynchroniczne w pierwszej kolejności. To znaczy. Zwykle powodem, dla którego uruchamiasz różne rzeczy w wątku UI, jest fakt, że twój wątek interfejsu użytkownika nie może czekać na zakończenie operacji. Ale tutaj, dla każdej z tych operacji, akt wysyłania operacji do puli wątków, a następnie przechwytywanie z kontynuacją po jej zakończeniu, może dodać do twojego programu tyle samo wydajności, co sama operacja.

To jest dla , że zalecam, aby w ogóle nie przeszkadzał z asynchroniczną wersją metod. Wystarczy bezpośrednio wywołać metody Create(), Delete() i Move() z interfejsu użytkownika.

(Uwaga: jeden wyjątek od powyższego jest, jeśli do czynienia z udziału sieciowego lub różnych objętościach, gdzie Move() obejmuje faktycznie kopiowania danych więc nawet tam, to wielka ogromna „to zależy” Podobnie, podczas Delete() i Create().. Zwykle byłaby szybka nawet w sieci, mogliby zająć trochę czasu, jeśli operacja rzeczywiście zakończy się niepowodzeniem.Możesz rzeczywiście mieć dobry przypadek użycia do uruchamiania operacji asynchronicznie tam.

+0

Jako sidenote, używając 'Guard.FileExists (sourceFileName)' synchronicznie jest prawdopodobnie źle. Jeśli 'sourceFileName' znajduje się na innym komputerze, który nie odpowiada, powróciłeś do punktu 0 :-) – xanatos

+0

@xanatos: tak, zgadzam się również z tą obserwacją. Bez obejrzenia implementacji nie można na pewno wiedzieć, ale wydaje się, że można bezpiecznie założyć, że to tylko delegowanie do 'File.Exists()' i wyrzucanie wyjątku, jeśli zwraca 'false'. –

+0

@Peter Duniho: W WinRT wszystkie operacje na plikach są asynchroniczne, więc zastanawiałem się, czy nie jest to również korzystne dla typowych aplikacji .NET. Ok asynchroniczna implementacja File.Create jest wątpliwa, ale usuwanie i przenoszenie może być długotrwałe, nawet jeśli operacja się powiedzie. Czy Twoim zdaniem dobrym podejściem byłoby po prostu wdrożenie synchronizacji Move async i Create/Delete? – Fabe