В окне формы Windows несколько событий могут запускать асинхронный метод. Этот метод загружает файл и кэширует его. Моя проблема в том, что я хочу, чтобы этот метод был выполнен один раз. Другими словами, я хочу предотвратить многократную загрузку файла.
Если метод загрузки файла запускается дважды, я хочу, чтобы второй вызов дождался файла (или дождался выполнения первого метода).
Есть ли у кого-нибудь идеи, как этого добиться?
ОБНОВИТЬ: Я просто пытаюсь предотвратить ненужные загрузки. В моем случае, когда клиент наводит указатель мыши на элемент в ListBox более чем на пару миллисекунд, мы начинаем загрузку. Мы предполагаем, что пользователь щелкнет и запросит файл. Что потенциально может случиться, так это то, что пользователь удерживает указатель мыши на элементе в течение одной секунды, а затем щелкает. В этом случае начинаются две загрузки. Я ищу лучший способ справиться с таким сценарием.
ОБНОВЛЕНИЕ 2:: есть вероятность, что пользователь наведет указатель мыши на несколько элементов. В результате произойдет несколько загрузок. Я не очень серьезно отношусь к этому сценарию, но прямо сейчас, если мы столкнемся с таким сценарием, мы не отказываемся от загрузки. Файл будет загружен (файлы обычно составляют около 50-100 КБ), а затем будет кэширован.
Более подробная информация поможет. Вы пытаетесь гарантировать, что пропускная способность не распределяется между двумя конкурирующими загрузками, или избегаете ненужных загрузок одного и того же ресурса?
Можете ли вы скачать сразу несколько файлов? Если они передумают до завершения загрузки, откажетесь ли вы от первой загрузки и начнете новую?
Если вы действительно можете загружать только один файл за раз, я бы отключил обработчик асинхронных событий во время его работы и повторно подключил его, когда он закончился.





Вы можете просто заключить вызов метода в оператор блокировки, подобный этому
private static readonly Object padLock = new Object();
...
lock(padLock)
{
YourMethod();
}
В вопросе указано, что метод является асинхронным, IOW он быстро возвращается после отправки запроса на загрузку. Следовательно, этот ответ ничего не дает.
Это может быть правдой, но он также заявляет, что он хочет, чтобы второй запрос ждал завершения первого ... тем самым сводя на нет любые возможности асинхронности.
Фактически, второй асинхронный вызов, который понимает, что первый все еще обрабатывается, останется асинхронным с точки зрения вызывающей стороны. Для этого потребуется, чтобы все проверки выполнялись внутри самого асинхронного метода.
Возможно, я слишком поспешил с ответом и предположил, что кое-что мне не следует делать ... Я подожду более подробной информации (в надежде не потерять за это время другие голоса :))
Я не уверен, как это будет сделано в C#, но в java вы должны синхронизировать частный статический конечный объект в классе перед загрузкой файла. Это заблокирует любые дальнейшие запросы, пока не будет завершен текущий. Затем вы можете проверить, был ли загружен файл или нет, и действовать соответствующим образом.
private static final Object lock = new Object();
private File theFile;
public method() {
synchronized(lock) {
if (theFile != null) {
//download the file
}
}
}
synchronize {} в Java примерно эквивалентен lock {} в C#
Я был почти уверен, что есть эквивалент, просто не знал, что это такое.
Сохраняйте состояние того, что происходит в переменной формы, и пусть ваш асинхронный метод проверяет это состояние, прежде чем он что-либо сделает. Однако убедитесь, что вы синхронизируете доступ к нему! Для этого подходят мьютексы и семафоры.
Если вы можете загружать разные файлы одновременно, вам нужно будет отслеживать, что загружается, в списке для справки.
Если за один раз можно загрузить только один файл, и вы не хотите ставить вещи в очередь, вы можете просто отключить событие, пока что-то загружается, и повторно подключить его, когда загрузка будет завершена.
Вообще говоря, вам следует по возможности использовать в .Net оператор lock {}. Семафоры и мьютексы слишком надежны для такого рода ситуаций.
Я бы согласился, если бы проблема была определена более четко. Я много гадал.
Есть идеи, почему это было отклонено? Мне это кажется вполне разумным ответом.
Итак, если я хорошо понимаю, вы говорите мне сделать что-то подобное: if (this.IsUpdating == false) {lock (padLock) {if (this.IsUpdating == false) {...}}}. Подойдет ли здесь шаблон двойной проверки?
Вы должны положить весь чек в замок. Хотя, конечно, нужно уточнить, чего вы хотите.
Я добавил несколько комментариев к вашему вопросу - мне нужно еще немного
Я просто добавил новые детали. Еще раз спасибо!
Вот фиктивная реализация, которая поддерживает загрузку нескольких файлов:
Dictionary<string, object> downloadLocks = new Dictionary<string, object>();
void DownloadFile(string localFile, string url)
{
object fileLock;
lock (downloadLocks)
{
if (!downloadLocks.TryGetValue(url, out fileLock))
{
fileLock = new object();
downloadLocks[url] = fileLock;
}
}
lock (fileLock)
{
// check if file is already downloaded
// if not then download file
}
}
В общем, я согласен с Майкл, используйте lock вокруг кода, который фактически получает файл. Однако, если сначала происходит одно событие, и вы всегда можете загрузить файл, рассмотрите возможность использования Фьючерсы. В исходном случае запускайте будущий бег
Future<String> file = InThe.Future<String>(delegate { return LoadFile(); });
и в любом другом случае ждите будущего значения
DoSomethingWith(file.Value);
Если вы хотите, чтобы один поток ждал, пока другой поток завершит задачу, вы, вероятно, захотите использовать ManualResetEvent. Может быть, примерно так:
private ManualResetEvent downloadCompleted = new ManualResetEvent();
private bool downloadStarted = false;
public void Download()
{
bool doTheDownload = false;
lock(downloadCompleted)
{
if (!downloadStarted)
{
downloadCompleted.Reset();
downloadStarted = true;
doTheDownload = true;
}
}
if (doTheDownload)
{
// Code to do the download
lock(downloadCompleted)
{
downloadStarted = false;
}
// When finished notify anyone waiting.
downloadCompleted.Set();
}
else
{
// Wait until it is done...
downloadCompleted.WaitOne();
}
}
обратите внимание, что это будет поддерживать только одну загрузку за раз, если вы хотите 2 одновременных загрузки разных файлов и только 2 потока, возможно, потоки и очереди с синхронизацией являются лучшим выбором
Я согласен ... но на самом деле я просто пытался проиллюстрировать использование ResetEvents, а не реализовывать за него весь проект. :)
Если вы можете исправить некоторые из моих предположений (см. Ниже), я могу обновить свой ответ с помощью кода.