На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
[!] Как относитесь к модерированию на этом форуме? Выскажите свое мнение здесь
Модераторы: Qraizer
Страницы: (16) « Первая ... 8 9 [10] 11 12 ...  15 16 все  ( Перейти к последнему сообщению )  
> Thread-safe shared_ptr , Реализация потоко-безопасного SharedPtr
    Цитата ЫукпШ @
    Исключение может возникнуть везде

    И кстати, не потому ли ты упомянул об этих исключениях, что они у тебя регулярно возникают?
    Вообще, так быть не должно. Это следствие неправильно написанного кода. Windows здесь генерит исключение, линукс бы тупо падал по segmentation fault. Но плохо одинаково.
    Я тебе показал классы, если ты их смотрел, в которых исключений при обращении к указателю не будет.
    Сообщение отредактировано: Олег М -
      Цитата Олег М @
      Цитата ЫукпШ @
      Исключение может возникнуть везде

      И кстати, не потому ли ты упомянул об этих исключениях, что они у тебя регулярно возникают?

      Какое странное предположение.
      Тем более, что об исключительных ситуациях начал разговор именно ты.
      ---
      Возникают исключительные ситуации или нет, всё должно быть
      готово к их перехвату и получению диагностики.
      Цена ничтожная, а польза немалая.
      ---
      Ты же не выбрасываешь все иголки и нитки под предлогом того,
      в данный момент пуговицы не болтаются ?
      Сообщение отредактировано: ЫукпШ -
        Цитата ЫукпШ @
        Какое странное предположение.
        Тем более, что об исключительных ситуациях начал разговор именно ты.


        Во-первых, ты так и не ответил на вопрос - возникают ли у тебя исключения при обращении содержимому указателя, либо при операции delete? Насколько я понимаю, очень часто.
        Во-вторых, ничего странного в этом нет. Мне бы даже и в голову не пришло сказать, что исключения возникают везде. Они возникают только когда вызывается оператор throw. Оператор delete и разыменование указателя этого не делают.

        При таких случаях вызываются системные виндовские исключения, которые, если ты их не отлавливаешь в __try/__except, преобразуются компилятором и выглядят как обычные сишные исключения. Если ты не вызываешь такие исключения специально (есть такая методика, см Рихтера), это означает что у тебя где-то ошибка в коде - ты не проверяешь значение указателя на null, обращаешься к удалённому указателю и т.д. В твоём случае это, обращение к указателям, происходит потому что ты их не синхронизируешь в потоках. Т.е. у тебя там есть какие-то попытки, но они явно не работают.

        Думаю, у тебя так ещё куча утечек памяти. Как следствие всего этого.

        Я тебе написал код, который все эти проблемы решает - синхронизация, утечки памяти и т.д. Если б ты вместо того, чтоб втирать мне про какие-то пуговицы, удосужился прочитать его и, соответственно, исправить свой, тебе же было бы легче.
          Цитата Олег М @
          Цитата ЫукпШ @
          Какое странное предположение.
          Тем более, что об исключительных ситуациях начал разговор именно ты.


          Во-первых, ты так и не ответил на вопрос - возникают ли у тебя исключения при обращении содержимому указателя, либо при операции delete? Насколько я понимаю, очень часто.

          Ответ простой - никогда ещё я не получал исключений, когда использовал этот
          шаблонный класс в многопоточном приложении.
          Хотя если посмотреть на дату, класс написан буквально к этому обсуждению.
          Но прежде чем показывать исходник я конечно проверил, как это работает.
          ---
          Что касается перехвата исключений.
          Используя __try/__except действительно можно получить диагностику в виде контекста.
          Однако, перехватить любое исключение посредством try/catch тоже можно.
          Для этого у компилера от Микрософт существует ключ "/eha".
          В этом случае тоже будет использован SEH, только диагностики не получишь.
          Так что, если речь идёт о критике принятого решения, критиковать
          надо само наличие блоков try/catch, поскольку в этом случае исключение будет перехвачено,
          а диагностика получена не будет.
          ---
          Скучно разговаривать - откуда ты можешь знать про утечки памяти и исключения у меня ?
          Чтобы выяснить наличие утечек в данном случае нет необходимости проводить сложные исследования.
          Достаточно получить диагностику конструкторов/деструкторов создаваемых объектов.
          Сообщение отредактировано: ЫукпШ -
            Цитата ЫукпШ @
            Скучно разговаривать - откуда ты можешь знать про утечки памяти и исключения у меня ?


            Обычно по коду видно, где могут возникнуть проблемы, а где нет, даже без тестов.

            Кстати, пытаюсь сейчас переделать свои указатели под Windows, 32бит, vs2010. Там у меня тоже куча древних серверов, которые как раз и выдают подобные exceptions (если б падали, как в линуксе, уже давно бы починил). Не так просто, оказывается.
              Цитата Олег М @
              Обычно по коду видно, где могут возникнуть проблемы, а где нет, даже без тестов.

              "Наука начинается с измерения".©
              Для программирования это значит, что самый верный способ - потрассировать, проверить, как работает.

              Добавлено
              Цитата Олег М @
              Кстати, пытаюсь сейчас переделать свои указатели под Windows, 32бит, vs2010. Там у меня тоже куча древних серверов, которые как раз и выдают подобные exceptions

              Это как-же ?
              В Линуксе я никак не могу перехватить все исключения.
              Там нет SEH.
              Падение чего-то предполагает долгое и упорное исследование.
              Кроме того, Виндус некоторые ошибки иногда прощает, Линукс - нет.
              ---
              Для Виндуса делаем так:
              Выставляем флаг /eha.
              Окружаем, значит, все поточные функции блоками __try/__except с диагностикой.
              Если кто-то из них "верещит", начинаем работать.
              Можно по-ползать по памяти, изучая исполнимый код отладчиком, но это долго и муторно.
              Можно тупо ловить исключение блоками try/catch, уже без диагностики, по алгоритму "ловли льва в африке".
              Научное исследование превращается в примитивную и чисто техничекую работу.
              Не всегда конечно, но почти.
              Сообщение отредактировано: ЫукпШ -
                Цитата ЫукпШ @
                Падение чего-то предполагает долгое и упорное исследование.
                Кроме того, Виндус некоторые ошибки иногда прощает, Линукс - нет.


                Ну, чтоб понять что твой код будет падать, мне достаточно было беглого взгляда. На собеседованиях и то более сложные задачи предлагают. Это во–первых.
                Во–вторых, я вроде как говорил, что seh исключений в рогамме быть не должно, это сигнал о том, что у тебя ошибка в коде, твоя.
                В потоках никакие отладчики и никакие флаги тебе не помогут. Поможет лишь тупое разглядывание и анализ собственного кода.

                Если хочешь, я выложу виндовскии код указателей. Но при условии, что ты мне вызовешь там seh прерывание
                  Цитата Олег М @
                  Ну, чтоб понять что твой код будет падать, мне достаточно было беглого взгляда.

                  Т.е. собеседование ты провалил.
                  Чтобы быть совсем уверенным, я встроил этот класс в своё приложение, которое должно
                  работать 24 часа, 365 дней в году. Никаких исключений обнаружить не удалось.

                  Добавлено
                  Цитата Олег М @
                  В потоках никакие отладчики и никакие флаги тебе не помогут.

                  Откуда ты знаешь ?
                  Если возникнет исключение, я получу контекст, имя файла и стоку в исходниках, в которой
                  производится вызов фильтра. Если я утверждаю это так определённо, то потому, что неоднократно
                  это делал.
                  ---
                  Определить место, где произошло исключение можно достаточно легко.
                  А вот понять "почему" - действительно не всегда просто. Это "да".

                  Вот я взял, да и поделил на 0.
                  Вот полученная диагностика:
                  Цитата

                  00000017 23:39:42 [2476] FILE=.\biWth.cpp, Line=159
                  00000018 23:39:42 [2476] EXCEPTION_INT_DIVIDE_BY_ZERO, ExAddr=004132B6
                  00000019 23:39:42 [2476] CF =0001003F DR0=00000000 DR1=00000000 DR2=00000000
                  00000020 23:39:42 [2476] DR3=00000000 DR6=00000000 DR7=00000000 EAX=0000000A
                  00000021 23:39:42 [2476] EBX=00A1CC40 ECX=00000000 EDX=00000000 EBP=00E4FF68
                  00000022 23:39:42 [2476] EIP=004132B6 EDI=7C91043E ESI=00A1CC40 ESP=00E4FF14
                  00000023 23:39:42 [2476] EF =00010246 SCS=0000001B SDS=00000023 SFS=0000003B
                  00000024 23:39:42 [2476] SGS=00000000 SSS=00000023 SES=00000023 ...=00000000


                  Добавлено
                  Цитата Олег М @
                  Если хочешь, я выложу виндовскии код указателей. Но при условии, что ты мне вызовешь там seh прерывание

                  А зачем ?
                  Сделай как в книге Рихтера. Внутри блока __try/__except подели на 0
                  или разименуй нулевой указатель.
                  Сообщение отредактировано: ЫукпШ -
                    Цитата ЫукпШ @
                    Откуда ты знаешь ?
                    Если возникнет исключение, я получу контекст, имя файла и стоку в исходниках, в которой
                    производится вызов фильтра. Если я утверждаю это так определённо, то потому, что неоднократно
                    это делал.


                    Ну, ты мне так и не ответил, какие именно методы должны быть потокобезопасными.
                    Думаю, что вот эти три:
                    ShPtrIntLockedTh<T>::ShPtrIntLockedTh(const ShPtrIntLockedTh& p)
                    ShPtrIntLockedTh<T>::DisConnect(void)/ShPtrIntLockedTh<T>::DisConnectSlave(void)
                    ShPtrIntLockedTh<T>::operator=(const ShPtrIntLockedTh& p)

                    Рассмотрим сценарий, когда один поток вызывает конструктор копирования, а другой ShPtrIntLockedTh<T>::DisConnect:
                    1-й: вызывает DisConnect(), потом DisConnectSlave(), который возвращает true
                    2-й: вызывает ShPtrIntLockedTh(const ShPtrIntLockedTh& p), проверяет p.StatPtr, который != null, делает StatPtr = p.StatPtr;
                    1-й: делает delete StatPtr
                    2-й: вызывает StatPtr->IncCount(); Т.е. пытается изменить удалённый объект

                    В этом случае тебе повезёт, если в этой точке возникнет исключение. А так - последствия неопределённые, и ты ни одним отладчиком не сможешь найти причину.
                      Цитата Олег М @
                      Потокобезопасный указатель означает, что мне не надо использовать дополнительных средств, ресурсов и блокировок при работе с этим классом. Это не от хорошей жизни, поверь.

                      Поверю. Но не до конца. Окончательно поверю только данным профилировщика. Ты его запускал? :) Пока же останусь при мнении, что экономия на блокировках при работе "с этим классом" - это экономия на спичках. :)

                      Цитата Олег М @
                      Сервер, вернее один из серверов, - по сути просто кэширует данные, которые хранятся в базе данных и отдаёт их либо по rpc-запросу, либо по подписке клиенту. Данные в кэше обновляются асинхронно, по сигналам из базы. Ещё, по запросам выполняются ява-скрипты, которые получают данные из памяти сервера, делают какие-то вычисления и возвращают результат.
                      Он не работает с браузеры, запросы идут от веб-серверов, по сокетам, TCP.

                      Цитата Олег М @
                      У меня не лочатся списки подписчиков. Все нотификации выполняются асинхронно и независимо от других. Я не могу допустить, чтоб все клиенты блокировались из-за одного, либо чтоб данные перестали обновляться, из-за того, что какая-то процедура тормозит. Вернее, определённые блокировки есть, но в основном для того чтоб контролировать количество запущенных потоков.

                      Цитата Олег М @
                      У меня не веб-сервис. 4000 (тут, признаюсь, немного приврал, такой цифры я не видел. В среднем - 3500) это коннекты по веб-сокетам, постоянные. RPC-коннекты я не считаю, но в среднем где-то один в секунду.

                      Итак, java-скрипты, RPC, веб-сокеты, асинхронная работа с БД... Асинхронное оповещение через потоки означает пул потоков и очередь на отправку уведомлений в нём. Нет, определённо, шаренный указатель на клиента - это самое узкое место. :D Ещё раз: профилировщик это подтвердил? :) Вот возьми конкретно этот форум, где мы с тобой общаемся. Он довольно коряво написан на PHP, хостится на апаче, сервер - не самый мощный (не такой мощный, как твой). Только вчера он отрабатывал по одному запросу в секунду, на каждый запрос делая семнадцать обращений к базе, рассылая почту, форматируя страницы, и тэ дэ. И успевая всё это сделать за 250 миллисекунд. В свои лучшие времена (посмотри статистику) на ещё более худшем железе от отбивал по десятку клиентских запросов в секунду. На PHP. Не оптимизированном. Без всякого C++ и прочего. Без клиентских скриптов (с их минимумом). В общем, твои показатели не то, чтобы впечатляющие. Ты VTune уже скачал, или продолжаешь визуальный анализ кода на предмет возможных узких мест?

                      Цитата Олег М @
                      Во-вторых, меня не беспокоит скорость подписки/отписки - куда ещё быстрее?. Это должно беспокоить тебя.

                      А вот нужно ли быстрее? А если нужно, то в каком конкретно месте?

                      Цитата Олег М @
                      У меня сервер работает на машине с 4 процессорами и 16Гб памяти. Хранит данные о 50тыс аккаунтов и 15тыс инструментов, постоянно обновляет их и рассылает подписчикам. Как ты думаешь у меня там есть возможность делать что-то не оптимально (хотя, конечно, косячу регулярно)?

                      Есть. Возможность есть всегда. :D А вот где именно оптимизировать - лучше всего подскажет профилировщик.

                      Но это я всё ёрничаю. На самом деле, не далее, как вчера, обсуждали с коллегой именно такой тип указателя, о котором ты говоришь. Но... Есть нюанс. Атомики при его реализации будут реальными спичками, ибо там (как я и описывал в своих предыдущих комментариях) вызов методов обёрнут лочкой, при смене указателя вызываются callback'и, есть wait-функции доступности, и прочие подобные вещи. Вот так вот неоптимально. :) Да и вот сел сегодня, думаю, дай напишу этот самый "потокобезопасный" shared_pointer. Принялся интерфейс weak/shared_ptr'а проектировать, и понял, что ну нет смысла именно эту связку делать потокобезопасной. Не для того она предназначена. Там везде одни сплошные копии самой обёртки, и reset, сделанный на одной копии не аффектит другие. И не должен аффетить: http://ideone.com/LzYlYZ То есть, по сути, тебе не аналог связки weak_ptr/shared_ptr нужен, а что-то "немножко" другое. Что-то надёжное, что ты можешь сохранить один раз, использовать из разных потоков, где-то - лоча, а где-то - перезаписывая.
                        Цитата Flex Ferrum @
                        Поверю. Но не до конца. Окончательно поверю только данным профилировщика. Ты его запускал? Пока же останусь при мнении, что экономия на блокировках при работе "с этим классом" - это экономия на спичках


                        Нет, не запускал и не планирую. Что он мне может сказать, чего я и так не знаю?
                        У меня нет проблем с производительностью и временем обработки запроса, которое в среднем ~0мс. Как ты правильно заметил, количество запросов и объёмы данных у меня не те, чтоб мне понадобилось то, что ты называешь "агрессивной оптимизацией". Тем более я слабо представляю, как можно профилировать сервер с кучей потоков и коннектов. Там в результатах нужно будет разбираться дольше.

                        Основная проблема у меня - это надёжность и сопровождение. А то что ты называешь "спичками" уменьшает код в разы, соответственно повышает его сопровождаемость. Надёжность тоже, за счёт того что при разработке используется меньшее количество решении и нужно соблюдать меньшее количество правил.


                        Цитата Flex Ferrum @
                        Нет, определённо, шаренный указатель на клиента - это самое узкое место. Ещё раз: профилировщик это подтвердил?

                        Нет, не определённо. Ты свой код через профилировщик прогонял? Он подтвердил, что функция Unregister() у тебя будет самым слабым местом, при увеличении количества подписчиков?



                        Цитата Flex Ferrum @
                        На самом деле, не далее, как вчера, обсуждали с коллегой именно такой тип указателя, о котором ты говоришь. Но... Есть нюанс. Атомики при его реализации будут реальными спичками, ибо там (как я и описывал в своих предыдущих комментариях) вызов методов обёрнут лочкой, при смене указателя вызываются callback'и, есть wait-функции доступности, и прочие подобные вещи. Вот так вот неоптимально.


                        Вот за это спасибо. Хотелось бы послушать ваше обсуждение.
                        Что-то я не понял, что ты имеешь ввиду под "спичками" - то ли что это зря потраченное время на оптимизацию, то ли это не оптимизирует работу, а наоборот?


                        Цитата Flex Ferrum @
                        Да и вот сел сегодня, думаю, дай напишу этот самый "потокобезопасный" shared_pointer. Принялся интерфейс weak/shared_ptr'а проектировать, и понял, что ну нет смысла именно эту связку делать потокобезопасной. Не для того она предназначена.


                        Задача shared_ptr сводится к тому, чтоб контролировать время жизни объекта, weak_ptr - чтобы быть в курсе жив объект или нет. Было бы здорово, если бы ты попытался написать его.
                          Кх-гм... Почему-то представляется диалог:
                          - Доктор, я себя что-то неважно чувствую.
                          - А, голубчик, у вас волчанка.
                          - Доктор, с чего вы взяли? Вы ведь даже анализов у меня не взяли!
                          - А у вас, вон, глаза красные.

                          Примерно так выглядят разговоры об оптимизации кода без привлечения данных от инструментальных средств. :-? И именно поэтому "предварительную оптимизацию" называют корнем всех зол - ведь можно потратить массу времени увлеченно оптимизируя то, что в этом совершенно не нуждается.
                          А профилировать сервер просто: запускаешь его под профайлером, даёшь тестовую нагрузку нужного профиля и снимаешь данные. У тебя ведь есть тестовый стенд, и тестовый же генератор нагрузки?
                          Оптимизация списка подписчиков, о которой я говорил, была сделана по результатам анализа логов и собранных статистических показателей работы системы - размеры очередей, количество входящих запросов, объёмы исходящих данных, креш-дампы, и т. п. А до этого, в предыдущем проекте - по результатам жёсткого профилирования, где всё крутилась в одном процессе, скорости были на порядки выше и данные снимались не только VTune'ом, но и с помощью Windows Performance Toolkit. Оптимизацией под контролем инструментальных средств я занимаюсь лет десять, а то и больше.

                          На счёт указателя - есть подозрение, что он тебе не очень подойдёт. Там не будет "спичек" (в смысле, мелких оптимизаций, сделанных без видимой причины), зато будет rw-lock. Такая вот жирная пессимизация. :)

                          Добавлено
                          И это, в твоей задаче я не вижу именно shared_ptr. Ты не разделяешь владение своим объектом, не отправляешь копии указателя на него в "свободное" плаванье. Тебе нужен что-то в стиле RemoteObjectHolder. Это не столько указатель, сколько сервисный класс, контролирующий доступ к объекту из разных потоков, его операбельность и прочие подобные фишки. Ну, насколько я понял твою задачу. Ты не планируешь создавать копии этого холдера, ты предполагаешь работать только с одной из всех потоков. Так ведь?
                            Цитата Flex Ferrum @
                            Примерно так выглядят разговоры об оптимизации кода без привлечения данных от инструментальных средств. И именно поэтому "предварительную оптимизацию" называют корнем всех зол - ведь можно потратить массу времени увлеченно оптимизируя то, что в этом совершенно не нуждается.


                            Чаще я наблюдал другую проблему - люди с помощью кучи инструментальных средств пытаются найти ошибку или оптимизировать абсолютно дикий код. При этом ужасно гордятся тем, как они ловко пользуются этими средствами. Это касается и с++ и баз данных и прочего тоже.

                            Цитата Flex Ferrum @
                            На счёт указателя - есть подозрение, что он тебе не очень подойдёт. Там не будет "спичек" (в смысле, мелких оптимизаций, сделанных без видимой причины), зато будет rw-lock. Такая вот жирная пессимизация.


                            Где ты нашёл у меня rw-lock?

                            Цитата Flex Ferrum @
                            И это, в твоей задаче я не вижу именно shared_ptr. Ты не разделяешь владение своим объектом, не отправляешь копии указателя на него в "свободное" плаванье. Тебе нужен что-то в стиле RemoteObjectHolder. Это не столько указатель, сколько сервисный класс, контролирующий доступ к объекту из разных потоков, его операбельность и прочие подобные фишки. Ну, насколько я понял твою задачу. Ты не планируешь создавать копии этого холдера, ты предполагаешь работать только с одной из всех потоков. Так ведь?


                            Не очень понял, но наверное что-то типа того. Что RemoteObjectHolder делает, чего не умеет shared_ptr?
                              Цитата Олег М @
                              Где ты нашёл у меня rw-lock?

                              У тебя его нет. Он у меня будет.

                              Цитата Олег М @
                              Не очень понял, но наверное что-то типа того. Что RemoteObjectHolder делает, чего не умеет shared_ptr?

                              Самокопироваться, например. То есть интерфейс этого класса явно рассчитан на то, что один и тот же его экземпляр будут использовать в разных потоках. shared_ptr ориентирован на копирование с последующем независимым поведением каждой из копии (я это показывал в примере выше).


                              Цитата Олег М @
                              Чаще я наблюдал другую проблему - люди с помощью кучи инструментальных средств пытаются найти ошибку или оптимизировать абсолютно дикий код. При этом ужасно гордятся тем, как они ловко пользуются этими средствами. Это касается и с++ и баз данных и прочего тоже.

                              Ну, не знаю. Когда при мне занимались оптимизацией (и когда я сам в ней участвовал) задача была вписаться в те или иные требования по производительности и потребляемым при этом ресурсам. И ставилась она не в формулировках "давайте заоптимизируем вот этот кусок кода", а "видишь график распределения времени по функциям? Вот необходимо, чтобы вот эта, вот эта и вот та части занимали не больше 20% от общего времени работы системы". Или: "При 200 активных подключениях длительностью не менее полминуты и десяти новых подключениях в секунду объём потребления процессора не превышал 30%". Или: "При входящем траффике в 1Gb/sec время генерации исходящих событий не должно превышать 10 сек. при 80% потребления CPU". Как-то так. И от этого плясали.
                              Сообщение отредактировано: Flex Ferrum -
                                Цитата Flex Ferrum @

                                Кх-гм... Почему-то представляется диалог:
                                - Доктор, я себя что-то неважно чувствую.


                                Вообще, если ты не заметил, то я пришёл сюда не жаловаться на что либо. Я просил всего лишь оценить реализацию класса. Неважно зачем и как я его буду использовать. Но для того, чтобы принимать решения, мне нужно знать недостатки класса, его тонкие места.

                                Чтобы пользоваться классом, нужно представлять, как он реализован. Я встречал людей, которые кричали, что C++ отстой, а C рулит и приводили пример как быстро работает массив int[100] по сравнению с std::vector.

                                Добавлено
                                Цитата Flex Ferrum @
                                У тебя его нет. Он у меня будет


                                Именно поэтому я там и воспользовался счётчиком.

                                Добавлено
                                Цитата Flex Ferrum @
                                Самокопироваться, например. То есть интерфейс этого класса явно рассчитан на то, что один и тот же его экземпляр будут использовать в разных потоках. shared_ptr ориентирован на копирование с последующем независимым поведением каждой из копии (я это показывал в примере выше).


                                Я слабо представляю, как operator-> можно использовать в разных потоках
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:
                                Страницы: (16) « Первая ... 8 9 [10] 11 12 ...  15 16 все


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0615 ]   [ 16 queries used ]   [ Generated: 18.07.25, 18:44 GMT ]