Страницы

Поиск по вопросам

вторник, 2 октября 2018 г.

Алгоритм удаления временных файлов

Постараюсь описать задачу. Имеется 1 скрипт, который генерирует файлы в папку tmp/. После этого сразу же второй скрипт начинает отдавать файл клиенту (стартует загрузку на клиент) с поддержкой докачки (http_range), поэтому неизвестно, когда файл скачается.
После чего файл больше не нужен. Как организовать удаление файлов и по какому критерию? Пока только придумал удаление всех файлов во временной директории, время жизни которых дольше N минут.
P.S.: Я уже пытался выяснить, как определить, что файл скачался в этом вопросе


Ответ

Возможно, вариант отмороженный, но если через 10 мин можно открыть файл с помощью fopen($file, 'a'); - значит, его не качают в данный момент.
Как вариант - сделать хэндлер для загрузки, в котором разбирать Accept-Range, и делать примерно так
fseek($f, $acceptRangeStart); $flock = @fopen('__' . basename($fileToDownload) . $acceptRangeStart, 'w+'); @fclose($flock); // создаем лок-файл echo fread($f, $acceptRangeBytes); @unlink('__' . basename($fileToDownload) . $acceptRangeStart); $finished = true; foreach (scandir('tmp') as $fn) { if (strpos($fn, '__' . basename($fileToDownload)) !== false) { $finished = false; // если идет докачка, сбрасываем break; } } if ($finished) { // если лок-файлов не найдено unlink($fileToDownload); // удаляем основной файл }
А чем вам, кстати, не нравится вариант, который используют почти все файлообменники? (Ссылка для скачивания будет доступна в течение суток и только для этого компьютера)

Как правильно указать лицензию в своем приложении?

Нужно ли добавлять в приложение перечень используемых библиотек и лицензии на их распространение ?
Как вы решаете подобные вопросы ?
Раскройте, пожалуйста, ответ для двух вариантов:
инди разработчик (с наличием или отсутствием монетизации приложения); приложение, выполненное, к примеру, на фрилансе.


Ответ

Для тех, кому еще это нужно. Пример для пары библиотек Google
Текст, который необходимо указать для них:
Apache License Version 2.0 Android Support Library Copyright (c) Google Inc. All rights reserved. Google Play Services Copyright (c) Google Inc. All rights reserved.
И так далее продолжаете перечень. После нужно указать текст ниже:
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Текст взят с официального сайта Apache
Когда закончите с Apache, и у Вас будут другие типы лицензий в приложении, то Вам нужно будет повторить принцип описания и для них.
Тип лицензии Перечень То, что требуется указать при распространении.
// Прошу меня поправить, если я в чем-то ошибся. Как понял принцип, так его и описал в ответе. Надеюсь, что он пригодится многим))

&&&&& и прочие операторные странности

Недавно тут пробегал вопрос о последовательностях операторов ++++ и ----. Навеяло...
У Саттера в "Новых сложных задачах" есть забавная задачка 33 о том, сколько можно в корректном коде C++ использовать одинаковых символов того или иного оператора подряд (комментарии, препроцессор, макросы, литералы не рассматриваются).
Мой вопрос - с выходом стандарта C++11/14 изменился ли его ответ на эту задачу?
Как я понимаю, теперь последовательность >>>> становится бесконечной - в шаблонах теперь не требуется вставлять пробел...
Остальные операторы - вроде бы должны остаться неизменными.
Но теперь в C++ конструкция && приобрела еще один смысл. В связи с этим - вопрос: осталось ли неизменным решение Саттера с пятью & подряд или теперь его можно превзойти?
Это не соревнование, но если кто-то превзойдет Саттеровские решения - будет интересно посмотреть. Для справки: +-!~* - тривиальные бесконечные последовательности & - пять штук <| - по четыре ... - даже не смешно :)
Да, получить два %% или там ^^ тоже просто:
class X { public: void operator%(int); }; typedef void(X::*F)(int); void operator%(F,X){} int main(int argc, const char * argv[]) { X x; &X::operator%%x; }


Ответ

По моему мнению, использование новой семантики для && в c++11 не позволит построить более длинную цепочку, чем у Саттера (5 символов подряд). Причина, по которой я так считаю, опирается на возможные новые контексты использования &&
указание rvalue-ссылки для типа локальной переменной:
R&& r = makeR(); использование универсальной ссылки в шаблонной функции:
template void f(T&& param); использование универсальной ссылки для локальной auto переменной:
auto&& r = f(something); перегрузка функций-членов для rvalue/lvalue объектов:
struct S { void f() & {} void f() && {} };
Во всех упомянутых контекстах && используется при объявлении сущности. Указать тут больше, чем 2 & подряд, не представляется возможным. Компилятор будет сообщать, что нельзя сделать ссылку на ссылку. Например, для clang
'variable' declared as a reference to a reference
Для шаблонной функции или перегруженных для rvalue/lvalue указание типа при вызове явно не требуется, а определяется из типа выражения. Т.е. & при вызове не используются вовсе. А если и используются, то только для явного приведения, где больше двух & по-прежнему нельзя указать. Например:
static_cast(t);
При попытке указать больше - получим уже упомянутую ранее ошибку "ссылка на ссылку".
Т.о. побить рекорд Саттера для & появление новой семантики ссылок в c++11 не позволяет.

Почему при определенном имени программы ее запуск существенно замедляется?

С такой мистикой сталкиваюсь впервые. Программа - буквально пустышка. В прямом смысле - простая HelloWorld - это я к ней уже дошел по ходу экспериментов. Компилирую в Visual C++ 2015 уже тупо без ключиков совсем. Получаю mm.exe. Запускаю. Выполняется секунд 6. Выводит, что надо. Но теперь самое интересное - если я компилирую с другим именем, да просто переименовываю mm.exe в какое-нибудь rr.exe или mmm.exe :) - все нормально! Мгновенное выполнение...
Windows 7 x64. Никаких mm в пути (path) не замечено.
Update Код любой, например, сейчас этот:
#include int main(int argc, const char * argv[]) { std::cout << "Hello!
"; }
Что эта мистика может значить?


Ответ

Вынесено из комментариев:
@KoVadim: в виндовом реестре есть большая ветка, где для разных программ добавлены различные правила. Например, для некоторых старых игр можно использовать память уже после освобождения. И определяет оно по имени файла. Я так уже когда то намучился.
@Harry: И в самом деле нашел HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\GameUX, и там Might and Magic... Спасибо!

Многопоточное vs асинхронное сетевое программирование на практике

Как-то в комментариях VladD поделился информацией, что один из его коллег, сетевой программист, перешел от многопоточного к асинхронному сетевому программированию. Хотелось бы на примере конкретной задачи разобраться, насколько асинхронность выиграет у многопоточности.
Задача: возьмем один из простых сетевых протоколов - RFB. Нам нужно одновременно подключиться к 10 000 серверов с RFB на борту, и узнать версию RFB
Как это реализовать многопоточно - я знаю, но как это реализовать асинхронно? И на сколько, в данной задаче, асинхронность выиграет? Сама реализация RFB - не нужна, нужен пример выполнения 10 000 одновременных асинхронных запросов.

Протестировал 3 варианта кода:
Многопоточный (поменял разбивку списка на несколько - на потокобезопасную очередь, чтобы уровнять шансы) Асинхронный Асинхронный (паттерн Throttling)
Результаты (проверка 300 000 IP адресов):
Многопоточный: 3 минуты 18 секунд Асинхронный: 1 минута 27 секунд Асинхронный (паттерн Throttling): когда перевалило за 6 минут - закрыл программу и не стал измерять дальше. Скорости можно добиться только использовав в уровне параллелизма - размер всего списка, но тогда теряется смысл самого использования паттерна. Т.е. реализация от andreycha, если использовать уровень параллелизма меньше размера списка - работает дольше чем даже многопоточная версия. Возможно это просто моя ошибка, либо ошибка andreycha.
Вывод:
Стандартная асинхронная реализация работает более чем в 2 раза быстрее чем многопоточная.


Ответ

Про асинхронность и ее преимущества тут. Вкратце -- в то время, пока запрос ушел в сеть и не вернулся обратно, мы не блокируем потоки на нашем компьютере. Т.о. 10000 адресов можно вполне обработать, например, несколькими потоками.
Запускать 10000 одновременных запросов это, конечно, перебор. Но запускать, скажем, по полсотни-сотне одновременных запросов -- вполне нормально. Такой шаблон называется троттлингом -- throttling (или в автомобильных терминах -- дросселированием :D). Т.е. пропускаем весь объем заданий по-немногу. Такой подход позволяет несильно загружать канал при отправке запросов и машину при получении и разборе ответов. Примерный код может выглядеть так:
public async Task CheckServers() { var servers = new List(10000) { ... };
const int ConcurrencyLevel = 100;
// запускаем первые 100 запросов var tasks = servers.Take(ConcurrencyLevel).Select(GetVersion).ToList(); int nextIndex = ConcurrencyLevel;
while (tasks.Count > 0) { // дожидаемся завершения любого запроса var completedTask = await Task.WhenAny(tasks);
// удаляем его из списка tasks.Remove(completedTask);
// добавляем новый запрос, если таковые остались if (nextIndex < servers.Count) { tasks.Add(GetVersion(servers[nextIndex++])); }
string rfbVersion = await completedTask; // работаем с версией } }
private async Task GetVersion(string server) { // тут асинхронная реализация обращения к серверу по RFB и возвращение версии }
Важный вопрос состоит в правильной асинхронной реализации обращения к серверу по протоколу RFB. Если вы используете библиотеку -- она должна поддерживать асинхронность. Если вы реализуете эту функциональность сами (например, на сокетах) -- значит нужно пользоваться асинхронными функциями сокетов.
UPD
Выяснилось, что запросы/ответы у ТС настолько легковесные, что в данном случае троттлинг работает медленнее, чем если отправить сразу все запросы. Однако этот паттерн может быть по-прежнему полезен, когда необходимо ограничить количество исходящих запросов и/или количество обрабатываемых ответов (например, если разбор ответов сильно загружает процессор/требует много памяти).

В каком случае лучше использовать volatile?

Предположим, у меня имеется List, который одновременно читает один поток, а другой в него пишет. В данному случае что было бы правильнее и быстрее использовать: volatile или ConcurrentBag?


Ответ

О скорости можно говорить лишь тогда, когда у вас имплементирована функциональность, и профилирование показывает, что доступ к данным является bottleneck'ом. Поэтому давайте разберёмся с корректностью.
Начнём с того, что volatile мы не можем применить к списку — volatile-поля могут быть только элементарных типов. Даже если бы его можно было применить, что толку в синхронизации доступа к ссылке на контейнер? Гонки-то у нас по данным внутри контейнера.
ConcurrentBag — это более разумный выбор. Но это только если реально данные лишь добавляются в одном из потоков, а не модифицируются им. В зависимости от вашей точной задачи, может понадобиться ConcurrentBag (если не важен порядок) или ConcurrentQueue (если важен).
Если нужен доступ по индексу, или модификация данных в пишущем потоке (например: collection[i]++), не стоит искать выгоду в несколько микросекунд, проще и безопаснее использовать нормальный lock

Зачем при записи чисел в конце добавляют .0?

Часто в коде программ числа записываются в виде цифры с .0 на конце. Например:
C: double s = 1.0 / 6.0;
Delphi: var d: Single; begin d := 16.0 + 1.0; end;
JS: var t = 90.0;
Возможны примеры и на других языках, но для начала предлагаю ограничиться этими тремя
Какой смысл несет добавление .0 в данных случаях? В каких случаях .0 можно опускать, а в каких он меняет логику поведения программы?


Ответ

Если говорить в общем, то добавление .0 к числу, изменяет его тип и превращает его из целого, в дробное (число с плавающей точкой). Такое изменение типа числа влияет на то, как оно будет храниться в памяти и на то, как и какие операции с ним могут выполняться.
В JS добавление .0 абсолютно лишено всякого смысла, т.к. там все числа изначально являются дробными.
В Delphi так же не возникает необходимости вручную приводить целые числа к дробным, т.к. компилятор замечательно справляется с этим самостоятельно, там где это нужно. Операция деления двух целых всегда возвращает дробное число и его невозможно по ошибке присвоить целому. Для особых случаев, когда надо выполнить целочисленное деление, есть специальный оператор div
А вот в Си и некоторых других языках, и для целочисленного, и для обычного деления используется один и тот же оператор деления / который ведёт себя по разному, в зависимости от типа операндов:
если оба операнда целые, то и в результате деления будет целое (целочисленное деление): double i = 5 / 2; даст 2.0 (дробная часть результата операции отбрасывается, получается целое число, которое затем приводится к целевому типу double). если хоть один из операндов является дробным, то и результат будет дробным: double i = 5 / 2.0; даст 2.5.
В Си, вместо .0 перед числом можно указывать тип числа с плавающей точкой (float/double), т.е. выполнять приведение типа: double i = 5 / (double) 2; такая конструкция используется, если операнд не число, а переменная целочисленного типа:
int k = 2; double i = 5 / (double) k; // --> i = 2.5