Учитывая три задачи: FeedCat()
, SellHouse()
и BuyCar()
, есть два интересных случая: либо они все завершаются синхронно (по какой-то причине, возможно, кэширование или ошибка), либо они не делают.
Скажем, у нас есть вопрос:
Task<string> DoTheThings() {
Task<Cat> x = FeedCat();
Task<House> y = FeedCat();
Task<Tesla> z = FeedCat();
// what here?
}
. Теперь простой подход будет:
Task.WhenAll(x, y, z);
, но ... это не удобный для обработки результатов; мы обычно хотели бы await
, что:
async Task<string> DoTheThings() {
Task<Cat> x = FeedCat();
Task<House> y = FeedCat();
Task<Tesla> z = FeedCat();
await Task.WhenAll(x, y, z);
// presumably we want to do something with the results...
return DoWhatever(x.Result, y.Result, z.Result);
}
, но это делает много накладных расходов и выделяет различные массивы (включая массив params Task[]
) и списки (внутренне). Это работает, но это не очень хорошая ИМО. Во многих отношениях проще использовать операцию async
и только await
каждый по очереди:
async Task<string> DoTheThings() {
Task<Cat> x = FeedCat();
Task<House> y = FeedCat();
Task<Tesla> z = FeedCat();
// do something with the results...
return DoWhatever(await x, await y, await z);
}
Вопреки некоторым комментариям выше, использование await
вместо Task.WhenAll
делает не отличается от того, как выполняются задачи (одновременно, последовательно и т. д.). На самом высоком уровне Task.WhenAll
предшествует хорошей поддержке компилятора для async
/ await
и полезен , когда эти вещи не существовали . Это также полезно, когда у вас есть произвольный массив задач, а не 3 сдержанных задач.
Но: у нас все еще есть проблема, что async
/ await
генерирует много шума компилятора для продолжения , Если вероятность того, что задачи действительно завершится синхронно, , мы можем оптимизировать это, построив синхронный путь с асинхронным резервным копированием:
Task<string> DoTheThings() {
Task<Cat> x = FeedCat();
Task<House> y = FeedCat();
Task<Tesla> z = FeedCat();
if(x.Status == TaskStatus.RanToCompletion &&
y.Status == TaskStatus.RanToCompletion &&
z.Status == TaskStatus.RanToCompletion)
return Task.FromResult(
DoWhatever(a.Result, b.Result, c.Result));
// we can safely access .Result, as they are known
// to be ran-to-completion
return Awaited(x, y, z);
}
async Task Awaited(Task<Cat> a, Task<House> b, Task<Tesla> c) {
return DoWhatever(await x, await y, await z);
}
Этот «путь синхронизации с async fallback "становится все более распространенным, особенно в высокопроизводительном коде, где синхронные завершения относительно часты. Обратите внимание, что это вообще не поможет, если завершение всегда истинно асинхронно.
Дополнительные действия, которые применяются здесь:
1: с недавним C #, общий шаблон для async
метод резервного копирования обычно реализуется как локальная функция:
Task<string> DoTheThings() {
async Task<string> Awaited(Task<Cat> a, Task<House> b, Task<Tesla> c) {
return DoWhatever(await a, await b, await c);
}
Task<Cat> x = FeedCat();
Task<House> y = FeedCat();
Task<Tesla> z = FeedCat();
if(x.Status == TaskStatus.RanToCompletion &&
y.Status == TaskStatus.RanToCompletion &&
z.Status == TaskStatus.RanToCompletion)
return Task.FromResult(
DoWhatever(a.Result, b.Result, c.Result));
// we can safely access .Result, as they are known
// to be ran-to-completion
return Awaited(x, y, z);
}
2: предпочитает ValueTask<T>
- Task<T>
, если есть хорошие шансы на вещи когда-либо полностью синхронно со многими различными значениями возврата:
ValueTask<string> DoTheThings() {
async ValueTask<string> Awaited(ValueTask<Cat> a, Task<House> b, Task<Tesla> c) {
return DoWhatever(await a, await b, await c);
}
ValueTask<Cat> x = FeedCat();
ValueTask<House> y = FeedCat();
ValueTask<Tesla> z = FeedCat();
if(x.IsCompletedSuccessfully &&
y.IsCompletedSuccessfully &&
z.IsCompletedSuccessfully)
return new ValueTask<string>(
DoWhatever(a.Result, b.Result, c.Result));
// we can safely access .Result, as they are known
// to be ran-to-completion
return Awaited(x, y, z);
}
3: если возможно, предпочитайте IsCompletedSuccessfully
- Status == TaskStatus.RanToCompletion
; это теперь существует в .NET Core для Task
и везде для ValueTask<T>
Этот код работает с Tomcat 8.0:
File catalinaHome = new File("..."); // folder must exist
Tomcat tomcat = new Tomcat();
tomcat.setPort(8080); // HTTP port
tomcat.setBaseDir(catalinaHome.getAbsolutePath());
tomcat.getServer().addLifecycleListener(new VersionLoggerListener()); // nice to have
У вас есть два варианта. Автоматически развертывать любое веб-приложение в catalinaHome/webapps
:
// This magic line makes Tomcat look for WAR files in catalinaHome/webapps
// and automatically deploy them
tomcat.getHost().addLifecycleListener(new HostConfig());
Или вы можете вручную добавить WAR-архивы. Примечание. Они могут находиться где угодно на жестком диске.
// Manually add WAR archives to deploy.
// This allows to define the order in which the apps are discovered
// plus the context path.
File war = new File(...);
tomcat.addWebapp("/contextPath", war.getAbsolutePath());