Используя реализацию Task.WhenAny
, вы можете создать как перегрузку расширения, которая также получает фильтр.
Этот метод возвращает Task
, который будет завершен, когда любая из поставленных задач будет завершена, а результат передайте фильтр.
Что-то вроде этого:
static class TasksExtensions
{
public static Task<Task<T>> WhenAny<T>(this IList<Task<T>> tasks, Func<T, bool> filter)
{
CompleteOnInvokePromiseFilter<T> action = new CompleteOnInvokePromiseFilter<T>(filter);
bool flag = false;
for (int i = 0; i < tasks.Count; i++)
{
Task<T> completingTask = tasks[i];
if (!flag)
{
if (action.IsCompleted) flag = true;
else if (completingTask.IsCompleted)
{
action.Invoke(completingTask);
flag = true;
}
else completingTask.ContinueWith(t =>
{
action.Invoke(t);
});
}
}
return action.Task;
}
}
class CompleteOnInvokePromiseFilter<T>
{
private int firstTaskAlreadyCompleted;
private TaskCompletionSource<Task<T>> source;
private Func<T, bool> filter;
public CompleteOnInvokePromiseFilter(Func<T, bool> filter)
{
this.filter = filter;
source = new TaskCompletionSource<Task<T>>();
}
public void Invoke(Task<T> completingTask)
{
if (completingTask.Status == TaskStatus.RanToCompletion &&
filter(completingTask.Result) &&
Interlocked.CompareExchange(ref firstTaskAlreadyCompleted, 1, 0) == 0)
{
source.TrySetResult(completingTask);
}
}
public Task<Task<T>> Task { get { return source.Task; } }
public bool IsCompleted { get { return source.Task.IsCompleted; } }
}
Вы можете использовать этот метод расширения следующим образом:
List<Task<int>> tasks = new List<Task<int>>();
...Initialize Tasks...
var task = await tasks.WhenAny(x => x % 2 == 0);
//In your case would be something like tasks.WhenAny(b => b);
Попробуйте этот код.
Это - немного измененная версия Вашего кода.
1. Я удалил Консоль. WriteLine, поскольку это - вероятно, немного порядков величины медленнее, чем, что я пытаюсь измерить.
2. Я запускаю Секундомер перед циклом и останавливаю его прямо после, этот способ, которым я не теряю точность, если функция берет, например, 26,4 галочек для выполнения.
3. Путем Вы разделились, результат некоторыми повторениями был неправильным. Посмотрите то, что происходит, если у Вас есть 1 000 миллисекунд и 100 миллисекунд. В обеих ситуациях Вы получите 0 мс после деления его 1000000.
Stopwatch s = new Stopwatch();
var p = new { FirstName = "Bill", LastName = "Gates" };
int n = 1000000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0;
string result;
s.Start();
for (var i = 0; i < n; i++)
result = (p.FirstName + " " + p.LastName);
s.Stop();
cElapsedMilliseconds = s.ElapsedMilliseconds;
cElapsedTicks = s.ElapsedTicks;
s.Reset();
s.Start();
for (var i = 0; i < n; i++)
result = string.Format("{0} {1}", p.FirstName, p.LastName);
s.Stop();
fElapsedMilliseconds = s.ElapsedMilliseconds;
fElapsedTicks = s.ElapsedTicks;
s.Reset();
Console.Clear();
Console.WriteLine(n.ToString()+" x result = string.Format(\"{0} {1}\", p.FirstName, p.LastName); took: " + (fElapsedMilliseconds) + "ms - " + (fElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x result = (p.FirstName + \" \" + p.LastName); took: " + (cElapsedMilliseconds) + "ms - " + (cElapsedTicks) + " ticks");
Thread.Sleep(4000);
Те - мои результаты:
1000000 x заканчиваются = строка. Формат (" {0} {1} ", p. FirstName, p. LastName); взял: галочки на 618 мс - 2213706
1000000 x заканчиваются = (p. FirstName +" "+ p. LastName); взял: галочки на 166 мс - 595610
Мне было любопытно где StringBuilder, выдержанный с этими тестами. Результаты ниже...
class Program {
static void Main(string[] args) {
var p = new { FirstName = "Bill", LastName = "Gates" };
var tests = new[] {
new { Name = "Concat", Action = new Action(delegate() { string x = p.FirstName + " " + p.LastName; }) },
new { Name = "Format", Action = new Action(delegate() { string x = string.Format("{0} {1}", p.FirstName, p.LastName); }) },
new { Name = "StringBuilder", Action = new Action(delegate() {
StringBuilder sb = new StringBuilder();
sb.Append(p.FirstName);
sb.Append(" ");
sb.Append(p.LastName);
string x = sb.ToString();
}) }
};
var Watch = new Stopwatch();
foreach (var t in tests) {
for (int i = 0; i < 5; i++) {
Watch.Reset();
long Elapsed = ElapsedTicks(t.Action, Watch, 10000);
Console.WriteLine(string.Format("{0}: {1} ticks", t.Name, Elapsed.ToString()));
}
}
}
public static long ElapsedTicks(Action ActionDelg, Stopwatch Watch, int Iterations) {
Watch.Start();
for (int i = 0; i < Iterations; i++) {
ActionDelg();
}
Watch.Stop();
return Watch.ElapsedTicks / Iterations;
}
}
Результаты:
Concat: 406 ticks Concat: 356 ticks Concat: 411 ticks Concat: 299 ticks Concat: 266 ticks Format: 5269 ticks Format: 954 ticks Format: 1004 ticks Format: 984 ticks Format: 974 ticks StringBuilder: 629 ticks StringBuilder: 484 ticks StringBuilder: 482 ticks StringBuilder: 508 ticks StringBuilder: 504 ticks
Первый (формат) выглядит лучше мне. Это более читаемо, и Вы не создаете дополнительные временные строковые объекты.
О, и только для полноты, следующее является несколькими галочками быстрее, чем нормальная конкатенация:
Console.WriteLine(String.Concat(p.FirstName," ",p.LastName));
Я всегда шел строка. Формат () маршрут. Способность сохранить форматы в переменных как пример Nathan является большим преимуществом. В некоторых случаях я могу добавить переменную, но еще раз, чем 1 переменная связывается, я осуществляю рефакторинг для использования форматирования.
Мне на самом деле нравится первый потому что, когда существует много переменных, смешиваемых с текстом, который кажется легче прочитать в меня. Плюс, легче иметь дело с кавычками при использовании строки. Формат (), мм, формат. Вот достойный анализ из конкатенации строк.
Лично, второй как все, что Вы используете, находится в прямом порядке, в котором он будет произведен. Принимая во внимание, что с первым необходимо совпасть {0} и {1} с надлежащим var, который легко испортить.
, По крайней мере, это не настолько плохо как C++ sprintf, где, если Вы получаете тип переменной неправильно, все это аварийно завершится.
кроме того, так как второе все встроено, и это не должно делать никакого поиска и замены для весь эти {0} вещи, последний должен быть быстрее..., хотя я не знаю наверняка.
Хороший!
Просто добавил
s.Start();
for (var i = 0; i < n; i++)
result = string.Concat(p.FirstName, " ", p.LastName);
s.Stop();
ceElapsedMilliseconds = s.ElapsedMilliseconds;
ceElapsedTicks = s.ElapsedTicks;
s.Reset();
, И это еще быстрее (я предполагаю строку. Concat называют в обоих примерах, но первый требует своего рода перевода).
1000000 x result = string.Format("{0} {1}", p.FirstName, p.LastName); took: 249ms - 3571621 ticks
1000000 x result = (p.FirstName + " " + p.LastName); took: 65ms - 944948 ticks
1000000 x result = string.Concat(p.FirstName, " ", p.LastName); took: 54ms - 780524 ticks
Я предпочитаю второе также, но у меня нет рациональных аргументов в это время для поддержания той позиции.
Я думаю, что это зависит в большой степени от того, насколько сложный вывод. Я склонен выбирать, какой бы ни сценарий работает лучше всего в то время.
Выбор правильный инструмент на основе задания: D, Какой бы ни выглядит самым чистым!
Если Вы имеете дело с чем-то, что должно быть легко считать (и это - большая часть кода), я придерживался бы версии перегрузки оператора ЕСЛИ:
При по крайней мере двух из этих обстоятельств, я использовал бы StringBuilder вместо этого.
Я поражен, что столько людей сразу хочет найти код, который выполняет самое быстрое. , Если ОДИН МИЛЛИОН повторений ВСЕ ЕЩЕ берет меньше, чем секунда для обработки, будет этим ВСЕГДА примечательным конечному пользователю? Не очень вероятно.
Преждевременная оптимизация = СБОЙ.
я пошел бы с String.Format
опция, только потому, что она имеет большую часть смысла с архитектурной точки зрения. Я не забочусь о производительности, пока это не становится проблемой (и если бы это сделало, я спросил бы меня: я должен связать миллион имен сразу? Конечно, они не будут все соответствовать на экране...)
, Рассматривают, хочет ли Ваш клиент позже изменить его так, чтобы они могли настроить, отобразиться ли "Firstname Lastname"
или "Lastname, Firstname."
С Параметром формата, это легко - просто выгружают строку формата. С concat Вам будет нужен дополнительный код. Уверенный это не походит на грандиозное предприятие в этом конкретном примере, но экстраполирует.
Боже мой - после чтения одного из других ответов я пытался инвертировать порядок операций - настолько работающий конкатенацию сначала, тогда Строка. Формат...
Bill Gates
Console.WriteLine(p.FirstName + " " + p.LastName); took: 8ms - 30488 ticks
Bill Gates
Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took: 0ms - 182 ticks
, Таким образом, порядок операций имеет Огромное значение, или скорее самая первая операция ВСЕГДА намного медленнее.
Вот результаты выполнения, где операции завершаются несколько раз. Я попытался изменить заказы, но вещи обычно следуют тем же правилам, когда-то первый результат проигнорирован:
Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 5ms - 20335 ticks
Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 0ms - 156 ticks
Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 0ms - 122 ticks
Bill Gates
Console.WriteLine("{0} {1}", FirstName, LastName); took: 0ms - 181 ticks
Bill Gates
Console.WriteLine("{0} {1}", FirstName, LastName); took: 0ms - 122 ticks
Bill Gates
String.Concat(FirstName, " ", LastName); took: 0ms - 142 ticks
Bill Gates
String.Concat(FirstName, " ", LastName); took: 0ms - 117 ticks
, Поскольку Вы видите последующие выполнения того же метода (я осуществил рефакторинг код в 3 метода) инкрементно быстрее. Самое быстрое, кажется, Консоль. WriteLine (Строка. Concat (...)) метод, сопровождаемый нормальной конкатенацией, и затем отформатированными операциями.
начальная задержка запуска вероятна инициализация Консольного Потока как размещение Консоли. Writeline ("Запускаются!"), прежде чем первая операция заставляет все случаи подчиняться назад.
Вот являются мои результаты более чем 100 000 повторений:
Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took (avg): 0ms - 689 ticks
Console.WriteLine(p.FirstName + " " + p.LastName); took (avg): 0ms - 683 ticks
И вот код места размещения:
Stopwatch s = new Stopwatch();
var p = new { FirstName = "Bill", LastName = "Gates" };
//First print to remove the initial cost
Console.WriteLine(p.FirstName + " " + p.LastName);
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
int n = 100000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0;
for (var i = 0; i < n; i++)
{
s.Start();
Console.WriteLine(p.FirstName + " " + p.LastName);
s.Stop();
cElapsedMilliseconds += s.ElapsedMilliseconds;
cElapsedTicks += s.ElapsedTicks;
s.Reset();
s.Start();
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
s.Stop();
fElapsedMilliseconds += s.ElapsedMilliseconds;
fElapsedTicks += s.ElapsedTicks;
s.Reset();
}
Console.Clear();
Console.WriteLine("Console.WriteLine(\"{0} {1}\", p.FirstName, p.LastName); took (avg): " + (fElapsedMilliseconds / n) + "ms - " + (fElapsedTicks / n) + " ticks");
Console.WriteLine("Console.WriteLine(p.FirstName + \" \" + p.LastName); took (avg): " + (cElapsedMilliseconds / n) + "ms - " + (cElapsedTicks / n) + " ticks");
Так, я не знаю чей ответ отмечать как ответ:)
Я использовал бы Строку. Формат, но у меня также была бы строка формата в файлах ресурсов, таким образом, это может быть локализовано для других языков. Используя простую строку concat не позволяет Вам делать это. Очевидно, если Вы, привычка когда-либо должна локализовать ту строку, это не причина думать о. Это действительно зависит от того, для чего строка.
, Если бы это будет, показал пользователю, я использовал бы Строку. Формат, таким образом, я могу локализовать, если мне нужно к - и FxCop, проверит правописание его для меня на всякий случай:)
, Если бы это содержит числа или какие-либо другие нестроковые вещи (например, даты), я использовал бы Строку. Формат, потому что это дает мне больше управление форматированием .
, Если бы это для создания запроса как SQL, я использовал бы Linq.
, Если бы для конкатенации строк в цикле, я использовал бы StringBuilder для предотвращения проблем производительности.
, Если бы это для некоторого вывода, пользовательская привычка видит, и не собирается производить производительность, я использовал бы Строку. Формат, потому что я имею привычку использовать его так или иначе и я просто привык к нему:)
Для очень простого управления я использовал бы конкатенацию, но как только Вы добираетесь вне 2 или 3 Форматов элементов, становится более соответствующим IMO.
Другая причина предпочесть Строку. Формат - то, что строки.NET неизменны и делают его, этот путь создает меньше временных/промежуточных копий.
Конкатенация строк прекрасна в простом сценарии как этот - это более сложно с чем-либо более сложным, чем это, даже LastName, FirstName. С форматом Вы видите, сразу, чем заключительная структура строки будет при чтении кода с конкатенацией, кроме которой становится почти невозможно сразу различить конечный результат (с очень простым примером как этот).
то, Что это означает в конечном счете, - то, что, когда Вы возвращаетесь для внесения изменения в формат строки, у Вас или будет способность зайти без предупреждения и внести несколько корректировок в строку формата, или сморщить Ваш лоб и начать перемещать все виды средств доступа свойства, смешанных с текстом, который, более вероятно, представит проблемы.
при использовании.NET 3.5 можно использовать дополнительный метод как этот и получить легкое течение, неподготовленный синтаксис как это:
string str = "{0} {1} is my friend. {3}, {2} is my boss.".FormatWith(prop1,prop2,prop3,prop4);
Наконец, когда Ваше приложение растет в сложности, можно решить, что для нормального поддержания строк в приложении Вы хотите переместить их в файл ресурсов для локализации или просто в статического помощника. Этого будет НАМНОГО легче достигнуть, если Вы последовательно использовали форматы, и Ваш код может быть вполне просто пересмотрен для использования чего-то как
string name = String.Format(ApplicationStrings.General.InformalUserNameFormat,this.FirstName,this.LastName);
Для основной конкатенации строк я обычно использую второй стиль - легче читать и более простой. Однако, если я делаю более сложную строковую комбинацию, я обычно выбираю Строку. Формат.
Строка. Формат экономит на большом количестве кавычек и плюсов...
Console.WriteLine("User {0} accessed {1} on {2}.", user.Name, fileName, timestamp);
vs
Console.WriteLine("User " + user.Name + " accessed " + fileName + " on " + timestamp + ".");
Только несколько сохраненные charicters, но я думаю в этом примере, формат делает его намного более чистым.
Лучший тест должен был бы следить за Вашим использованием памяти Perfmon и счетчики памяти CLR. Мое понимание - то, что целая причина Вы хотите использовать Строку. Формат вместо того, чтобы просто связать строки, так как строки неизменны, Вы излишне обременяете сборщик "мусора" временными строками, которые должны быть исправлены в следующей передаче.
StringBuilder и Строка. Формат, хотя потенциально медленнее, является большей эффективной памятью.
Обычно я предпочитаю первого, как особенно, когда строки становятся длинными, может быть намного легче читать.
другое преимущество, я верю одной из производительности, поскольку последний на самом деле выполняет 2 строковых оператора создания прежде, чем передать заключительную строку Консоли. Метод записи. Строка. Формат использует StringBuilder под покрытиями, которым я верю, таким образом, нескольких конкатенаций избегают.
нужно отметить однако это, если параметры Вы являетесь передающими в Представить в виде строки. Формат (и другие такие методы как Консоль. Запишите), типы значения тогда, они будут упакованы, прежде чем передано в, который может обеспечить его собственные хиты производительности. Сообщение в блоге на этом здесь .
В то время как я полностью понимаю предпочтение стиля и выбранную конкатенацию для моего первого ответа частично на основе моего собственного предпочтения, часть моего решения была основана на мысли, что конкатенация будет быстрее. Так, из любопытства я протестировал его, и результаты колебались, специально для такой маленькой строки.
Используя следующий код:
System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch();
var p = new { FirstName = "Bill", LastName = "Gates" };
s.Start();
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
s.Stop();
Console.WriteLine("Console.WriteLine(\"{0} {1}\", p.FirstName, p.LastName); took: " + s.ElapsedMilliseconds + "ms - " + s.ElapsedTicks + " ticks");
s.Reset();
s.Start();
Console.WriteLine(p.FirstName + " " + p.LastName);
s.Stop();
Console.WriteLine("Console.WriteLine(p.FirstName + \" \" + p.LastName); took: " + s.ElapsedMilliseconds + "ms - " + s.ElapsedTicks + " ticks");
я получил следующие результаты:
Bill Gates
Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took: 2ms - 7280 ticks
Bill Gates
Console.WriteLine(p.FirstName + " " + p.LastName); took: 0ms - 67 ticks
Используя метод форматирования более чем в 100 раз медленнее!! Конкатенация даже не зарегистрировалась как 1 мс, который является, почему я произвел галочки таймера также.
I choose based on readability. I prefer the format option when there's some text around the variables. In this example:
Console.WriteLine("User {0} accessed {1} on {2}.",
user.Name, fileName, timestamp);
you understand the meaning even without variable names, whereas the concat is cluttered with quotes and + signs and confuses my eyes:
Console.WriteLine("User " + user.Name + " accessed " + fileName +
" on " + timestamp + ".");
(I borrowed Mike's example because I like it)
If the format string doesn't mean much without variable names, I have to use concat:
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
The format option makes me read the variable names and map them to the corresponding numbers. The concat option doesn't require that. I'm still confused by the quotes and + signs, but the alternative is worse. Ruby?
Console.WriteLine(p.FirstName + " " + p.LastName);
Performance wise, I expect the format option to be slower then the concat, since format requires the string to be parsed. I don't remember having to optimize this kind of instruction, but if I did, I'd look at string
methods like Concat()
and Join()
.
The other advantage with format is that the format string can be put in a configuration file. Very handy with error messages and UI text.
Если вы собираетесь локализовать результат, необходимо использовать String.Format, потому что на разных естественных языках данные могут даже не располагаться в одном и том же порядке.
Согласно подготовительному материалу MCSD, Microsoft предлагает использовать оператор + при работе с очень небольшим количеством конкатенаций (вероятно, от 2 до 4). Я до сих пор не знаю почему,
Пожалейте бедных переводчиков
Если вы знаете , ваше приложение останется на английском языке, тогда хорошо, сохраните тики часов. Однако во многих культурах, например, в адресах обычно встречается фамилия и имя.
Поэтому используйте string.Format ()
, особенно если ваше приложение когда-либо будет размещено в любом месте, где английский не является первым языком.
На самом деле я вчера запустил эти тесты, но становилось поздно, таким образом, я не помещал свои ответы.
нижняя строка кажется, что они берут обоих то же время в среднем. Я сделал тест более чем 100 000 повторений.
я попробую StringBuilder также, и я отправлю код и результаты, когда я возвращусь домой.