Я совсем запутался...
Asp.Net MVC - создание веб-приложений, Web Api - сервисов. Не могли бы вы объяснить для каких задач пишутся сервисы, а для каких веб приложения? Насколько я понимаю, если у нас клиент планируется не только веб, но еще, например, WPF-приложение, мобильное, то выбор должен пасть в сторону сервисов. Конкретно для моего случая: имеется некое десктопное приложение еще на WinWorms. Хочется его перевести в веб. И встает логичный вопрос что использовать: Asp.Net Web Api + AngularJs или Asp.Net MVC? Буду благодарен за любую информацию.
Ответ
Бакэнд для AngularJs можно написать хоть на ASP.NET MVC, хоть на Asp.Net Web Api, хоть на WCF (да, WCF тоже умеет делать REST-сервисы). Основная разница между ними в следующем: ASP.NET MVC подходит для генерации страниц на стороне сервера (но не мешает работать и клиентским фреймворкам). Выбрав связку Angular.js + Web Api, вы навсегда ограничите себя исключительно клиентской генерацией страниц, приобретя заодно возможные проблемы с индексацией поисковиками. Взамен - у вашего сайта автоматически появится API, которое можно сделать публичным. Думаю, начать надо с выяснения ответов на следующие вопросы:
Сколько человек будут работать над проектом и какие технологии им знакомы?
Насколько само ваше веб-приложение укладывается в концепцию HTTP? Оно будет состоять из набора отдельных динамически генерируемых страниц или из одной (или нескольких) страниц, содержащих формы, контролы и экраны? Пример приложения первого вида - Википедия. Пример приложения второго вида - Gmail. 2а. Предполагается ли, что страница никогда не будет обновляться полностью? 2б. Сможет ли пользователь открыть одновременно 2 части вашего приложения в одной вкладке? (+ ко второму виду) 2в. Понадобится ли пользователю делиться ссылкой с другими или ставить ссылку в закладки? (+ к первому виду)
Нужно ли вам публичное API?
Здравствуйте, после регистрации пользователя в приложении, как правильно хранить пароль пользователя? Если sharedpreferences - это наилучший вариант, можете объяснить почему, и можно ли взломать, и получить данные пользователя?
Ответ
данные типа String хранятся в SharedPreferences в явном виде, практически в текстовом файле. Получить к нему доступ и прочитать эти данные - не очень сложная задача. Для предотвращения доступа к данным используйте шифрование. Как Core Java (пакеты crypto и security), так и фреймворк Android (класс AccountManger к примеру) предоставляют достаточно инструментов (классов) для надежного шифрования информации.
Способ же сохранения зашифрованных данных отходит на второй план - это может быть БД, преференсы, файл и тп., как удобнее для конкретной задачи (например БД оправдана при хранении нескольких однотипных данных и слишком избыточна для только одного логина-пароля).
Как определить что список ListView прокручен до конца?
Ответ
listView.setOnScrollListener(new AbsListView.OnScrollListener() { @Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE
&& (listView.getLastVisiblePosition() - listView.getHeaderViewsCount() -
listView.getFooterViewsCount()) >= (adapter.getCount() - 1)) { // Now your listview has hit the bottom
}
} @Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { }
});
Уже не раз замечаю подобный код #if FUSE_TRACE
#define TRACE(x...) fprintf(stderr,x)
#else
#define TRACE(x...) do {} while (0)
#endif
Почему именно так, а не #if FUSE_TRACE
#define TRACE(x...) fprintf(stderr,x)
#else
#define TRACE(x...)
#endif
Насколько я понял do {} while (0) транслируется в NOP инструкцию, но зачем она здесь?
Ответ
Макросы - вещь достаточно тонкая. Представьте ситуацию, кто-то написал код (пример условный) (но лучше так не пишете именно из-за этого). for (int i = 0;i < 10; i++)
TRACE("ok"), s+=i;
do_any();
Здесь всё будет корректно работать.
В релиз режиме если мы будем использовать как вы предлагаете #define TRACE(x...)
Произойдёт следующее, код преобразуется в if (something)
, s+=i;
do_any();
И он не скомпилируется. Так же не скомпилируется expr? TRACE("y") : TRACE("n");
Однако, и с использованием макроса #define TRACE(x...) do {} while (0) есть аналогичные проблемы. Я лично использую следующий макрос в таких целях: #define TRACE(x...) ((void)0)
Либо же использую отдельную функцию (а не макрос) для вывода логов.
Возьмём самый распространенный вариант - смайлики. Их очень много (~1500) и отображаются все одновременно, как лучше их выводить?
На сервер храниться одна большая картинка (спрайт) где все смайлики расположены друг за другом, у клиента в css расписаны расположение каждого смайлика (background-image и background-position) и в html документе с помощью class распознаются смайлики.
На сервер хранится каждая картинка смайлика отдельно, у клиента в css расписаны путь до каждого смайлика (background-image) и в html документе с помощью class распознаются смайлики.
На сервер хранятся изображения отдельно, у клиента только в html документе написаны путь до смайликов (img[src]).
И какие картинки лучше использовать svg или png? Возможно вы предложите ещё более правильный вариант, пожалуйста расскажите максимально подробно, почему именно так?
Второй вопрос заключается в генерации этого списка смайликов, как лучше его генерировать?
Сгенерировать заранее и хранить на сервере в виде шаблона/html документа и подгружать его через ajax и вставлять в html страницу, когда это нужно.
Хранить html код в JS коде, в виде строки и вставлять когда это нужно.
Генерировать html код из заранее составленного массива хранящийся в js коде.
Генерировать код из массива хранящейся на сервер и полученного через ajax.
Так же прошу рассказывать максимально подробно. Под правильным вариантом подразумевается не только правильно по написанию кода, но и по быстроте загрузки смайликов и нагрузки на обе стороны (сервер и клиент).
Ответ
Куда проще пожертвовать загрузкой одним спрайтом на N Mb, чем открывать 1500 соединений. Спрайт после первой загрузки в кэш и тяжёлое неудобство исчезнет.
В HTTP2 попроще в этом вопросе, но настроить сервер грамотно нужно для выигрыша скорости.
Выходит для спрайта лучше юзать одну картинку с background-position и классами.
Какой формат лучше - вопрос достаточно сложный, сильно зависящий от конкретной ситуации и предпочтений, для спрайтов лично я обычно использую png - разумный баланс качества/размера.
Генерировать как картинку, которую прописать background-image, браузер сам подтянет, JS вообще не нужен. Use the grunt-spritesmith, Luke!
P. S. Инлайнить в css - это зарядить револьвер и поднести к ноге: поддержка сложнее, код распухает, кэшировать картинки нельзя (можно файлы стилей, конечно, но их может автоматически генерировать сервер, выгоды мало).
Однажды я попробовал и отказался.
Есть область с кучей разбросанных точек, нам нужно объединить в области те точки, которые находятся на расстоянии меньше определенной константы друг от друга. Как-то вот так:
Ответ
написал свой велосипед на javascript, может кому еще пригодится
Как восстановить удалённые файлы и каталоги в дистрибутиве debian gnu/linux?
Ответ
программы для восстановления (из одноимённых пакетов): testdisk, extundelete (только для файловых систем ext), foremost, scalpel возможно, некоторые из них и позволяют работать с примонтированными файловыми системами, но безопаснее, конечно, этого не делать. и восстанавливать удалённые файлы безопаснее в другую файловую систему (примонтированную). т.е., если удалённые файлы находились, например, в корневой файловой системе, то проще всего воспользоваться каким-нибудь live-cd/dvd/usb (к примеру, systemrescuecd), загрузившись с него. инструкции по использованию перечисленных программ можно почитать, например, здесь: http://help.ubuntu.ru/wiki/восстановление_данных
Мне нужно в этот header в поле для имени user поставить его имя, а в поле для email поставить соответственно email Я понимаю, что все стандартно и нужно найти по id view с которым хочешь работать и установить в него TextView. Но когда я нахожу view и ставлю text, то вылетает ошибка, что такого view еще нет. Это логично т.к. NavigationView еще не открыт... Так вот вопрос. Как отследить что NavigationView уже открыт и только после этого устанавливать text в view? Если я в onCreate() нахожу и устанавливаю текст TextView tvName = (TextView) findViewById(R.id.tvName);
TextView textEmail = (TextView) findViewById(R.id.textEmail); tvName.setText("test");
textEmail.setText("test");
то получаю такую ошибку в строке tvName.setText("test");
FATAL EXCEPTION: main
Process: com.example.android.camera2basic, PID: 32152
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.android.camera2basic/com.example.android.camera2basic.activities.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
Если применять такой подход, то ошибки нет, но текст остается таким как по умолчанию NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
RelativeLayout headerView = (RelativeLayout) LayoutInflater.from(this).inflate(R.layout.nav_header_mainsecond, null);
TextView tvName = (TextView) headerView.findViewById(R.id.tvName);
TextView textEmail = (TextView) headerView.findViewById(R.id.textEmail);
tvName.setText("name");
textEmail.setText("email");
Ответ
Вам надо:
Получить NavigationView
NavigationView nv = ...;
Получить разметку его Header
View header = nv.getHeaderView(0);
Найти нужные поля и с ними что-то сделать
TextView tv = (TextView)header.findViewById(...);
Здравствуйте.
При клике на ссылку появляются пункты меню, и если после этого кликнуть в любое место экрана (кроме ссылки которая активирует меню) - меню пропадает. Каким образом сделать чтоб меню пропадало еще и при повторном клике на эту же ссылку?
.menu {
display: none;
padding: 3px;
}
.menu a {
display: block;
margin: 1px;
padding: 10px;
}
.active_menu {
display: block;
}
.active_menu:focus ~ .menu,
.active_menu:active ~ .menu,
.menu:hover {
display: block;
}
Развернуть меню
Использую библиотеку react-router для навигации в SPA на базе React. Само приложение имеет вид: application.jsx import React from 'react';
import { Router, Route, browserHistory } from 'react-router';
import Home from './home.jsx'; export default React.createClass({
render() {
// Объект пользователя. Здесь не важно, откуда именно он берется.
const user = {name: 'FooBarBaz'}; return (
);
}
});
home.jsx import React from 'react'; export default React.createClass({
render() {
return (
You are {this.props.user.name}
);
}
});
Вопрос заключается в том, как именно пробросить объект пользователя (user) компоненту Home?
Использую react-router@2.4.1 и react@15.1.0
Ответ
Реакт позволяет использовать чистые функции в качестве компонентов. Это дает вам возможность обернуть компонент Home добавив туда любые свойства. Код приложения может выглядеть, например, так: import React from 'react';
import { Router, Route, browserHistory } from 'react-router';
import Home from './home.jsx'; export default React.createClass({
render() {
const user = {name: 'FooBarBaz'}; // Это всего лишь обертка для компонента Home, позволяющая передавать
// дополнительные параметры через замыкание.
const WrappedHome = function(props) {
// Конструкция "{...props}" нужна, чтобы не потерять
// параметры, переданные от компонента Route
return ();
}; // Обратите внимание на замену "Home" на "WrappedHome" в параметре
// "component".
return (
);
}
});
Начал изучение магических методов PHP. С большинством все понятно. Но вот __destruct(), я не могу понять практическое применение его именно в PHP. В каких случаях он нужен вообще? Время жизни объектов же крайне не большое. Какая практическая польза? Всем спасибо за ответы. Конкретно определить самый правильный не могу. Много интересных вещей написано в комментариях.
Ответ
Деструктор позволяет вашим объектам предоставлять гарантии по управлению ресурсами. В деструкторе вы можете написать логику, которая так или иначе - во время сборки мусора или при выключении VM - будет выполнена, и не оставит ваше приложение в неконсистентном состоянии. Сама логика может быть произвольной - чаще всего в пример приводят файл, который необходимо закрыть. Клиентский код может забыть это сделать, и тогда работу за него произведет деструктор. В качестве более интересного примера можно привести ссылочную систему: например, в системе есть кэш с коротким временем жизни, который самоуничтожается после того, как последний клиент отключился. В этом случае в деструкторе клиента такого кэша должна выполняться логика дерегистрации, чтобы кэш вовремя смог освободить оперативную память. При этом все нельзя забывать, что машина, на которой выполняется код, ненадежна и может упасть прямо посередине какой-либо транзакции - в этом случае код в деструкторе не выполнится.
Добрый вечер, возник вопрос как сделать чтобы своё готовое android приложение установленное на машине пользователя могло обновляться если я дополнил интерфейс или устранил баг и т д, как это прописать в коде ?
Ответ
Стандартными средствами и без рута - никак. Обычно это происходит так: вы выкладываете новую версию приложения в маркет, девайс юзера через маркет по расписанию (силами маркета, т.е. без вашего участия) проверяет все установленные приложения на предмет наличия обновлений и, если у юзера поставлены соответствующие настройки в маркете, маркет сам установит обновление
Согласно en-SO, если у вы программно скачаете новый apk, у коего версия больше версии текущего и идентичные пакет и сертификат, то вы можете запустить системный Intent на обновление приложения так: Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.fromFile(new File(pathToApk));
intent.setDataAndType(uri, "application/vnd.android.package-archive");
startActivity(intent);
Далее юзер должен будет, по идее, подтвердить установку обновления.
Если девайс рутован, то можно вот так запустить установку apk через систему без участия юзера: public static void installNewApk()
{
try
{
Runtime.getRuntime().exec(new String[] {"su", "-c", "pm install -r /mnt/internal/Download/fp.apk"});
}
catch (IOException e)
{
System.out.println(e.toString());
System.out.println("no root");
}
}
Реализовать же логику запуска этого интента/команды можно примерно так:
При первом запуске приложения запишите во внутренние ресурсы (sharedPrefereneces) номер версии.
С к-л переодичностью проверяйте есть ли у вас где-то на к-л сервере новая версия.
Если да - скачивайте apk и по завершении запускайте интент/команду.
Необходимо перенести объект типа std::unique_ptr из вектора в дек. Пример кода using UPtr = std::unique_ptr; std::deque d;
std::vector v; for (int i = 0; i < 5; ++i) {
v.emplace_back(new int(i));
} for (const auto& item : v) {
d.emplace_front(std::move(item));
}
Но неожиданно возникает ошибка компиляции: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete]'
{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
^
In file included from /usr/include/c++/5/memory:81:0,
from prog.cpp:4:
/usr/include/c++/5/bits/unique_ptr.h:356:7: note: declared here
unique_ptr(const unique_ptr&) = delete;
Насколько я понимаю, должен использоваться перемещающий конструктор, а не копирующий, но этого не происходит.
Ответ
Проблема оказалась в квалификаторе const
В цикле for (const auto& item : v) {
d.emplace_front(std::move(item));
}
переменная item имеет тип const std::unique_ptr&, соответственно при вызове std::move получаем тип const std::unique_ptr&&. Перемещающего конструктора, который бы принимал этот тип, нет, поэтому компилятор использует копирующий конструктор, что и приводит к ошибке.
Похожая ошибка рассмотрена у Скотта Мейерса в книге "Эффективный и современный C++" в разделе 5.1.
Простой вроде вопрос. Что означает регулярка "a^b" и можно ли ее найти в какой либо строке? import re
p = re.compile("a^b")
p.search("ab") --> None
p.search("a^b") --> None
p.search("a b") --> None
Если такое выражение действительно не соответствует никакой строке (действительно, как может символ a встретиться до начала проверяемой строки), то почему оно вообще компилируется? Или, перефразируя, меняется ли смысл символа ^/$ в зависимости от его положения в регулярном выражении (не внутри квадратных скобок)? Если нет, то почему? Вопрос безотносительно ЯП, python просто примера ради.
Ответ
^ означает "начало строки" вне зависимости от того, в какой части выражения встретилось. Так же как и $ - конец "строки". Но вот "начал" и "концов" в "строке" может быть несколько. Возьмем пример (на питоне, так на питоне): import re
p=re.compile("a.*^b",re.M+re.S)
p.search("abc abcd") --> None
p.search("abc bcd") --> <_sre.SRE_Match object; span=(0, 5), match='abc b'>
При компиляции выражения указаны флаги: re.S заставляет соответствовать любым символам, включая перевод каретки. re.M заставляет рассматривать "строку", как "многострочную" - т.е. ^ начинает соответствовать не только началу данных, но и точке после любого перевода каретки. Вот мы и получили, что "a" встретился до начала строки. Флаги можно задавать не только вторым параметром compile, но и внутри самой регулярки, конструкциями вроде (?sm). Думаю разработчикам регулярок влом проводить такой глубокий анализ (Есть флаги, нет, а когда они начали действовать), ради выдачи ошибок вроде "начала строки тут быть не может". Собственно по этому никто и не рискует рассматривать ^ и $ по разному, в зависимости от положения (кроме случаев с квадратными скобками, конечно). Пример на regexp101.com
Имеется колонка с текстом. На странице она одна и занимает примерно 70% от всей ширины страницы. Расположена по-центру.
Внутри текст и некоторые другие элементы, включая изображения.
Нужно сделать так, чтобы изображения занимали, скажем, 90% от ширины всей страницы.
Тут все дело вот в чем.
Использовать position: absolute не получится, потому как часть текста не будет видна, ибо будет находиться за картинкой.
Просто указать ширину равную, скажем, 180% для изображения тоже нельзя, иначе результат будет примерно следующий.
Также стоит учесть, что изображения могут быть разные по ширине. Из этого следует, что нужно сделать следующее: более мелкие изображения, которые не дотягивают до ширины колонки, нужно растягивать так, чтобы они занимали 100% ширины род. элемента. А те, что шире нужно растягивать так, чтобы занимали не более 90% от ширины всей страницы. Грубо говоря все это дело должно выглядеть примерно так:
img {
min-width: 100%; /* от ширины родителя */
max-width: 90%; /* от ширины всей страницы */
}
Как подобное можно реализовать? Допускается обрамлять img в div'ы и другие теги. Вспомнил об еще одном не подходящем способе: вариант обрамления всего содержимого кроме картинок в дополнительный тег и придание ему margin или padding. Этот вариант не подходит как минимум по двум причинам: задача заключается в том, чтобы разрешить картинке быть на всю (почти) ширину страницы, оставаясь при этом по-центру, а не сместить все кроме картинок, чтобы они казались растянутыми (1). И использование дополнительных тегов (что будет крайне неудобно делать каждый раз) (2).
Ответ
Достаточно использовать относительные единицы измерения.
body {
background-color: #eee;
}
.container {
background-color: #fff;
max-width: 70%;
margin: 0 auto;
padding: 10px; /* Для красоты */
}
img {
display: block;
transform: translateX(-50%);
margin-left: 50%;
max-width: 90vw;
min-width: 100%;
}
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ullam optio quasi sapiente labore enim nisi sit, facere adipisci corrupti distinctio. Fuga eos in debitis, recusandae rerum minus nulla sint doloribus ducimus dolore non, repellendus, ullam quia.
Molestiae enim ratione eaque?
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Velit nemo sit qui dolorem, minima quibusdam. Cumque aperiam sint odio culpa totam alias explicabo suscipit voluptates officiis adipisci temporibus, amet ipsum, vero voluptatibus consequatur
rerum, aut ullam. Sequi iusto, similique. Quis eaque, veritatis sapiente ea amet, veniam neque voluptatem repellendus possimus laboriosam quasi nam aliquam ullam similique, expedita. Quo aliquam molestias ratione nemo pariatur sed velit, sint nulla
tempore eveniet maiores nesciunt explicabo quas! Nesciunt, reprehenderit! Soluta voluptatibus perferendis quod nam.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Pariatur a saepe eveniet similique laudantium non aut delectus, facere libero ut vitae, enim tempore magnam eligendi adipisci cum minus. Eius doloremque molestias, dolores consequatur hic! Culpa
eveniet, velit nam dolor dignissimos!
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ullam optio quasi sapiente labore enim nisi sit, facere adipisci corrupti distinctio. Fuga eos in debitis, recusandae rerum minus nulla sint doloribus ducimus dolore non, repellendus, ullam quia.
Molestiae enim ratione eaque?
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Velit nemo sit qui dolorem, minima quibusdam. Cumque aperiam sint odio culpa totam alias explicabo suscipit voluptates officiis adipisci temporibus, amet ipsum, vero voluptatibus consequatur
rerum, aut ullam. Sequi iusto, similique. Quis eaque, veritatis sapiente ea amet, veniam neque voluptatem repellendus possimus laboriosam quasi nam aliquam ullam similique, expedita. Quo aliquam molestias ratione nemo pariatur sed velit, sint nulla
tempore eveniet maiores nesciunt explicabo quas! Nesciunt, reprehenderit! Soluta voluptatibus perferendis quod nam.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ullam optio quasi sapiente labore enim nisi sit, facere adipisci corrupti distinctio. Fuga eos in debitis, recusandae rerum minus nulla sint doloribus ducimus dolore non, repellendus, ullam quia.
Molestiae enim ratione eaque?
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Velit nemo sit qui dolorem, minima quibusdam. Cumque aperiam sint odio culpa totam alias explicabo suscipit voluptates officiis adipisci temporibus, amet ipsum, vero voluptatibus consequatur
rerum, aut ullam. Sequi iusto, similique. Quis eaque, veritatis sapiente ea amet, veniam neque voluptatem repellendus possimus laboriosam quasi nam aliquam ullam similique, expedita. Quo aliquam molestias ratione nemo pariatur sed velit, sint nulla
tempore eveniet maiores nesciunt explicabo quas! Nesciunt, reprehenderit! Soluta voluptatibus perferendis quod nam.
Разверните пример на всю страницу. Так лучше видно, что картинки 90% шириной.
При изучении многопоточности в Java столкнулся с непонятным для меня явлением при перестановке двух методов местами внутри одного блока if Ниже пример, в котором запускаются 10 потоков (ссылка на пример кода в GitHub). Их задача в порядке возрастания Id произвести некую работу, в данном случае вывести сообщение, содержащее Id потока и значение переменной в вспомогательном классе. Пример содержит три класса: Класс Main.
В нем в цикле создается 10 экземпляров класса ThreadExample, которые, в свою очередь являются элементами массива. Id первого экземпляра потока, до его старта заносится в переменную вспомогательного класса. После этого поочередно в цикле вызывается метод start всех потоков. package lan.example.thread_example; public class Main { static HelperSingletonBillPugh INSTANCE_TEST_THREAD = HelperSingletonBillPugh.getInstance();
static ThreadExample[] myThreads = new ThreadExample[10];
static final int COUNT = 10; public static void main(String[] args) { for (int i = 0; i < COUNT; i++) {
myThreads[i] = new ThreadExample();
if (i == 0) {
INSTANCE_TEST_THREAD.setCurentThreadId(myThreads[i].getId());
}
myThreads[i].start();
}
}
}
Класс HelperSingletonBillPugh.
Это вспомогательный хелпер класс, реализованрый по типу синглетона способом Била Пью. Вроде такой тип реализации синглетона считается потокобезопасным. Класс содержит переменную curentThreadId типа long с Id потока и публичные методы setCurentThreadId(long threadId), getCurentThreadId(), incremenCurentThreadId() для изменения, чтения и инкремента значения этой переменной. package lan.example.thread_example; public class HelperSingletonBillPugh { private HelperSingletonBillPugh() {
} private static class SingletonHandler {
private static final HelperSingletonBillPugh INSTANCE = new HelperSingletonBillPugh();
} public static HelperSingletonBillPugh getInstance() {
return SingletonHandler.INSTANCE;
} private long curentThreadId = 0L; // Id потока, который должен выполнить вывод сообщения public long getCurentThreadId() {
return this.curentThreadId;
} public void setCurentThreadId(long threadId) {
this.curentThreadId = threadId;
} public void incremenCurentThreadId() {
this.curentThreadId++;
}
}
Класс ThreadExample.
Класс наследник Thread. В нем вечный цикл, с паузами и постоянным сравнением значения getId() потока с переменной curentThreadId экземпляра вспомогательного класса HelperSingletonBillPugh. Если равенство выполняется, то выводится сообщение, переменная curentThreadId увеличивается на 1 и поток завершает работу. package lan.example.thread_example; public final class ThreadExample extends Thread { static HelperSingletonBillPugh INSTANCE_TEST_THREAD = HelperSingletonBillPugh.getInstance(); @Override
public void run() { while (true) { if (INSTANCE_TEST_THREAD.getCurentThreadId() == this.getId()) { // Раскомментируй следующую строку
//INSTANCE_TEST_THREAD.incremenCurentThreadId();
System.out.println("Print " + this.getName()
+ " ### ID:" + this.getId()
+ " ### getCurentThreadId: " + INSTANCE_TEST_THREAD.getCurentThreadId()); // Закоментируй следующую строку
INSTANCE_TEST_THREAD.incremenCurentThreadId(); break;
}
}
}
}
В данном примере кода сообщения выводятся последовательно и потоки завершают свою работу, все стабильно от запуска к запуску, вот такой вывод: Print Thread-1 ### ID:11 ### getCurentThreadId: 11
Print Thread-2 ### ID:12 ### getCurentThreadId: 12
Print Thread-3 ### ID:13 ### getCurentThreadId: 13
Print Thread-4 ### ID:14 ### getCurentThreadId: 14
Print Thread-5 ### ID:15 ### getCurentThreadId: 15
Print Thread-6 ### ID:16 ### getCurentThreadId: 16
Print Thread-7 ### ID:17 ### getCurentThreadId: 17
Print Thread-8 ### ID:18 ### getCurentThreadId: 18
Print Thread-9 ### ID:19 ### getCurentThreadId: 19
Print Thread-10 ### ID:20 ### getCurentThreadId: 20
Но стоит только переставить местами метод INSTANCE_TEST_THREAD.incremenCurentThreadId() и метод System.out.println() (в коде помечено где убрать и добавить коммент) и результат становится не стабильным и для меня не понятным, хотя перестановка осуществляется внутри блока if. Если быть точнее, то он не всегда стабильный, то есть, несколько запусков может пройти вполне корректно. Вот вывод одного из запусков после перестановки методов местами: Print Thread-1 ### ID:11 ### getCurentThreadId: 12
Print Thread-2 ### ID:12 ### getCurentThreadId: 15
Print Thread-6 ### ID:16 ### getCurentThreadId: 17
Print Thread-5 ### ID:15 ### getCurentThreadId: 16
Print Thread-4 ### ID:14 ### getCurentThreadId: 15
Print Thread-7 ### ID:17 ### getCurentThreadId: 21
Print Thread-3 ### ID:13 ### getCurentThreadId: 21
Print Thread-8 ### ID:18 ### getCurentThreadId: 21
Print Thread-9 ### ID:19 ### getCurentThreadId: 21
Print Thread-10 ### ID:20 ### getCurentThreadId: 21
Из примера видно, что потоки выводят сообщения хаотично и значение переменной getCurentThreadId временами отличается от Id текущего потока более чем на 1. Что-же происходит при изменении местами двух этих методов? Дело в том, что когда System.out.println() находится перед INSTANCE_TEST_THREAD.incremenCurentThreadId(), то у меня нет ни одного непредсказуемого результата. Можно ли считать это стабильной работой или все-таки эта стабильность обманчива и при неких обстоятельствах пример отработает не корректно? P/S. На всякий случай уточню. Я немного имею представление про Java Memory Model, оператор volatile, блок synchronize, про кэши данных и атомарность, правда глубоких знаний пока нет, но добиться гарантированной стабильной работы примера скорее всего смогу. Интересует именно понимание того, какие-такие серьезные изменения происходят при перестановки местами этих двух методов, так влияющие на результат работы примера. И, как следствие, можно ли считать стабильной работу примера в первом случае и почему? Проверял пример на разных операционных системах (Linux Mint 32 и 64 bit, Windows 10 64 bit), но правда только с Oracle JDK.
Ответ
Дело в том, что операция инкремента не является атомарной, а состоит из двух операций: чтения текущего значения и записи увеличенного значения. Если развернуть ее, то цикл в первом сценарии будет выглядеть так: while (...) {
<чтение id> // вывод на консоль
<чтение id> // инкремент, шаг 1
<запись id> // инкремент, шаг 2
}
Такое расположение операций вкупе с хитрым условием while по сути служит локом, дающим доступ к телу цикла только одному потоку. Сначала только первый поток заходит внутрь цикла, а остальные просто прокручивают его ("блокируются"). Когда первый поток записывает увеличенный id и выходит из цикла, то уже он начинает прокручивать цикл ("блокируется"), а второй поток заходит внутрь. И так по очереди для всех потоков. Именно за счет инкремента все потоки получают этот доступ последовательно, согласно своим номерам. Важным здесь является то, что запись id по сути освобождает "лок" текущего потока и "разрешает" выполнение другого потока. Пока она идет последней в теле цикла, проблем не возникает. Во втором сценарии цикл выглядит следующим образом: while (...) {
<чтение id> // инкремент, шаг 1
<запись id> // инкремент, шаг 2
<чтение id> // вывод на консоль
}
Теперь мы видим, что "лок" освобождается чуть раньше. К чему это может привести? К тому, что между записью id и вторым чтением id могут начать исполняться другие потоки, поскольку "лок" свободен и доступ к телу цикла может получить другой поток. Т.е. возникает классическая гонка, в результате которой может происходить следующее:
поток на втором чтении id может получить уже обновленное значение (это объясняет, например, почему Thread-2 выводит 15, а не 13)
вывод в консоль может перепутаться из-за того, что другие потоки "встревают" сразу после записи id while (...) {
<чтение id> // инкремент, шаг 1
<запись id> // инкремент, шаг 2
<возможное исполнение других потоков>
<чтение id> // вывод на консоль (потенциально уже нового значения)
}
Сценарий выполнения для приведенного вами в вопросе лога может выглядеть так: все потоки начинают выполнение
все потоки кроме потока 1 "блокируются" на условии while
поток 1 читает id (=11)
поток 1 увеличивает id (=12)
поток 2 "разблокируется"
поток 2 читает id (=12)
поток 1 читает id и выводит на консоль (=12)
поток 1 "засыпает"
поток 2 увеличивает id (=13)
поток 3 "разблокируется"
поток 3 читает id (=13)
поток 3 увеличивает id (=14)
поток 4 "разблокируется"
поток 4 читает id (=14)
поток 4 увеличивает id (=15)
поток 2 читает id и выводит на консоль (=15)
поток 2 "засыпает"
поток 5 "разблокируется"
поток 5 читает id (=15)
поток 5 увеличивает id (=16)
поток 6 "разблокируется"
поток 6 читает id (=16)
поток 6 увеличивает id (=17)
поток 6 читает id и выводит на консоль (=17)
поток 6 "засыпает" ...и так далее
Генерирую ASCII-строку следующим образом: def buildblock(size):
out_str = ''
for i in range(0, size):
a = random.randint(65,90)
out_str += chr(a)
return(out_str)
Но в таком случае генерируются символы только верхнего регистра. Например, FGLKJDSDH. А как сделать так, чтобы генерировались строки и нижнего и верхнего регистра одновременно? По типу YjhmrdVDgm
Ответ
Как-то так: import random
import string def buildblock(size):
return ''.join(random.choice(string.ascii_letters) for _ in range(size))
Можно добавить ещё и цифры: import random
import string def buildblock(size):
return ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(size))
Действующий код
Есть у меня простой класс-логгер: static class Logger
{
public delegate void Message(string msg); static public event Message OnMessage; static public void SendMessage(string msg)
{
OnMessage?.Invoke(msg);
}
}
Я из любых мест приложения отправляю ему сообщения по типу: Logger.SendMessage("Получена команда на запуск"); При загрузке window (WPF) я подписываюсь на события логгера и вывожу лог в textbox Пока приложение было однопоточным всё отлично работало, но теперь методы отлажены и надо всё распараллелить (идет обращение к 40 БД на разных хостах поэтому всё хорошо параллелится) Но теперь возникла проблема - при попытке прочитать отправленное сообщение из другого потока возникает Exception
"Вызывающий поток не может получить доступ к данному объекту, так как
владельцем этого объекта является другой поток."
Как это грамотно и с малой кровью исправить?
Ответ
Смотрите. Проблема в том, что события доставляются в том потоке, который отправляет события. Поэтому у вас подписчики событий получают событие каждый раз в разных потоках. Если подписчик — UI-код, который просто выводит текст в UI, то при приходе сообщения из неглавного потока происходит проблема. Есть несколько путей починки вашего кода.
Можно привязать логгер к главному потоку. При этом сообщения будут доставляться только в главном потоке, и соответственно UI-код будет всегда работать «как надо». static class Logger
{
static Lazy dispatcher =
new Lazy(() => Application.Current.Dispatcher); public delegate void Message(string msg); static public event Message OnMessage; static public void SendMessage(string msg)
{
if (dispatcher.Value.CheckAccess())
OnMessage?.Invoke(msg);
else
dispatcher.Value.InvokeAsync(() => OnMessage?.Invoke(msg));
}
}
Это, наверное, не самое лучшее архитектурное решение, т. к. при этом логгер получается зависимым от WPF, то есть модель получает зависимость от VM (что не позволит использовать её повторно в других программах). Зато этот метод решает задачу наиболее просто: другие переделки при этом не нужны.
Можно считать логгер не привязанным ни к какому потоку, тогда UI-код должен проверять, в каком потоке он запущен, и при необходимости пользоваться Dispatcher.InvokeAsync Logger.OnMessage += s =>
{
if (Dispatcher.CheckAccess())
LogContainer.Text += (s + " ");
else
Dispatcher.InvokeAsync(() => LogContainer.Text += (s + " "));
};
Это более правильный подход, но здесь придётся потенциально править все места, где происходит подписка на сообщения от логгера. Впрочем, такое место в программе, судя по всему, одно.
Вы можете использовать модные в этом сезоне Reactive Extensions, и переписать ваш класс на них: using System.Reactive.Linq;
using System.Reactive.Subjects; static class Logger
{
static ISubject subject = Subject.Synchronize(new Subject());
public static IObservable Messages => subject;
static public void SendMessage(string msg) => subject.OnNext(msg);
}
Подписка при этом выглядит так: Logger.Messages.ObserveOnDispatcher().Subscribe(s => LogContainer.Text += (s + " "));
Максимальная гибкость, LINQ на сообщениях, доставка в произвольный поток, навесные плюшки наподобие подавления слишком частых или повторяющихся сообщений поставляется в комплекте, бонусом ощущение собственной крутости, функциональности и трендовости. Минус — вам придётся-таки разобраться с этим самым Rx (муа-ха-ха!). Или это можно считать плюсом, да. (Think positive.) Не забудьте подключить из nuget System.Reactive.Core, System.Reactive.Interfaces, System.Reactive.Linq и System.Reactive.Windows.Threading
Записываю протокол событий вот таким скриптом: ");
flock($file,3);
fclose($file);
?>
Но возникает вопрос:
Что будет, если вызовов будет слишком много и со всех сторон?
Не выйдет ли, что пока я залочил файл в одном вызове скрипта, другой тоже попытался, ругнулся и ничего не записал?
Как предусмотреть это в коде?
Ответ
Функция flock() создает для файла флаг, указывающий на то, что с файлом идет работа. При этом другой скрипт или другая программа, умеющая распознавать подобный флаг, сделает соответствующие выводы и содержимого файла не нарушит. По умолчанию, данная функция будет ждать получения эксклюзивной блокировки на запись, это поведение можно изменить с помощью параметра LOCK_NB. Если у Вас будет многопоточная запись в файл, то каждый следующий запрос просто будет дольше выполняться из-за наличия блокировки. Кстати, весь ваш код можно перенести в одну конструкцию: file_put_contents('track.log', "$trnum \t" . date("Y-m-d h:ia") ." ", LOCK_EX | FILE_APPEND);
Оператор yield class UserCollection
{
public static IEnumerable Power()
{
yield return "Hello world!";
}
}
Оператор yield из .Net Reflector - перенесенный в рялии C# и Visual Studio class UserCollection
{
public static IEnumerable Power()
{
return new ClassPower(-2);
} private sealed class ClassPower : IEnumerable