none
Как отследить код, тормозящий приложение RRS feed

  • Pergunta

  • Доброй ночи! Такая проблема - есть класс отправки и принятия файлов с сервера по TCP. Этот класс был упрощен и переписан, после чего стало работать таким образом... 

    Первой загрузке файла класс долго думает. Если приостановить выполнение - часто останавливается на строке чтения данных с сервера Reader.ReadByte(). После 5-10 секунд отвисает и загружает файл.  В последующем, если снова пробовать загрузить какой-нибудь файл, никаких зависаний нет - все работает как нужно вплоть до закрытия программы. Проблема именно при первой загрузке после включения, которую я не могу отследить.

    Так-же такие странности бывают, что начинает загружать часть файла, потом загрузка замедляется, останавливается. И через 5-10 секунд все продолжается без проблем. Опять-же при первой загрузке файла.

    Как это можно отследить? Как понять после каких строк кода зависает?

    domingo, 21 de novembro de 2021 23:27

Respostas

  • Печаль беда. Конечно потоков больше 25 будет. Так как при загрузке файла через функцию создается новый поток на каждую из них. А одновременно ожидать могут и 25 и 50 файлов.

    Есть какой-нибудь способ ожидания результатов выполнения работы функции без создания дополнительного потока? Или придется опять возвращать старый класс для загрузки и уже через callback отслеживать завершение загрузки...

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

    Можете так же увеличить число потоков в пуле или ограничить число одновременно используемых потоков. Последнее более разумно так как очевидно пропускная способность сети конечна и общая скорость загрузки будет максимальной при определенном (и сравнительно небольшом) числе одновременных загрузок.

    Реализация второго подхода не сложна. Надо лишь создать очередь загрузок (например используя класс Queue<T>) и при исчерпании потоков ставить загрузки в очередь вместо немедленной попытки исполнения. Потоки после завершения загрузки будет проверять есть ли что либо в очереди и если есть то снимать очередную загрузку из очереди и выполнять ее. Это будет продолжаться до тех пор пока очередь не опустеет.


    This posting is provided "AS IS" with no warranties, and confers no rights.

    • Marcado como Resposta Siompc terça-feira, 23 de novembro de 2021 19:47
    terça-feira, 23 de novembro de 2021 19:22
    Moderador

Todas as Respostas

  • Вы уверены что проблема с кодом (который судя по описанию минимален), а не с типовыми задержками при работе сети? Например, с разрешением имени сервера, установлением подключения, обменом сертификатами и т.п.? 


    This posting is provided "AS IS" with no warranties, and confers no rights.

    segunda-feira, 22 de novembro de 2021 04:17
    Moderador
  • Именно в коде класса. Если использую для отправки / загрузки старый класс - работает отлично. На сервере никаких задержек. Сейчас пытаюсь с помощью точек останова просмотреть сколько каждая функция и процедура класса затрачивает время на выполнение. Может это даст подсказку где зарыта трабла

    Конечно подсказывать в данной ситуации очень сложно.. Я просто думал может есть лучше способы (помимо точек останова) посмотреть сколько затрачено времени участком кода. А то их расставлять +100500 штук то еще удовольствие


    • Editado Siompc segunda-feira, 22 de novembro de 2021 10:05
    segunda-feira, 22 de novembro de 2021 10:04
  • "Я просто думал может есть лучше способы (помимо точек останова) посмотреть сколько затрачено времени участком кода."

    Конечно есть: https://docs.microsoft.com/en-us/visualstudio/profiling/beginners-guide-to-performance-profiling?view=vs-2019

    Но профилирование, это в основном для более тонкого анализа. Когда речь о зависаниях в 5-10 секунд, остановки отладчиком вполне достаточно. Вам просто, возможно, надо отключить "Только мой код" в настройках отладки и разрешить отладку машинного кода, чтобы выявить, на каком методе сетевой библиотеки висит.

    segunda-feira, 22 de novembro de 2021 10:11
  • Именно в коде класса. Если использую для отправки / загрузки старый класс - работает отлично. На сервере никаких задержек. Сейчас пытаюсь с помощью точек останова просмотреть сколько каждая функция и процедура класса затрачивает время на выполнение. Может это даст подсказку где зарыта трабла

    Конечно подсказывать в данной ситуации очень сложно.. Я просто думал может есть лучше способы (помимо точек останова) посмотреть сколько затрачено времени участком кода. А то их расставлять +100500 штук то еще удовольствие


    Загрузка файла (например по HTTP) - это от силы 10 строк кода. Что именно вы поменяли в вашем классе? И с какой целью?

    Далее, вам надо использовать профайлер, он для этого и предназначен.


    This posting is provided "AS IS" with no warranties, and confers no rights.

    segunda-feira, 22 de novembro de 2021 17:39
    Moderador
  • Загрузка и отправка файлов по TCP, как я писал выше. Старый класс в общих чертах работает так:

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

    Ранее для загрузки файла требовалось вешать обработчик и ждать окончания загрузки нужного файла через него. Что бы убрать обработчик (использовать его только при необходимости) я реализовал загрузку через функцию. Теперь сам класс файла обрабатывает статусы буфера и ожидает окончание загрузки. После чего возвращает true или false если файл не загружен.

    Теперь что удалось проверить... Ставлю в цикле загрузить 100 разных файлов с сервера. Чем больше файлов я указываю в цикле, тем дольше идет первая задержка. Если 100 - зависание до 15-20 секунд. Причем интерфейс программы работает, НО! любое действие, например клик по кнопке, отправка команды в консоль или что другое - не работает. Кнопки нажимаются, консоль принимает команду, но не обрабатывает ее. Проходит 20 секунд - все начинает работать. Команды консоли сколько раз вводились, столько раз сразу начинают выполнятся. Тоже самое с кнопками. Нажали 10 раз открыть окно - спустя время одновременно откроется 10 окон. Ощущение словно не хватает обработчиков на остальные задачи.

    segunda-feira, 22 de novembro de 2021 20:58
  • И чему он возвращает статус? "Буферу потоков"? Как именно ожидается статус? Судя по вашему описанию функция вызывается синхронно блокируя обработку событий. 

    Что будет если убрать собственно загрузку, например заменив ее задержкой? Если проблема сохранилась, то очевидно дело не в загрузке, а в организации кода.

    Вообще говоря я вижу только два варианта:

    1. Загрузка выполняется на потоке, по окончании загрузки вызывается callback. 

    2. Загрузка выполняется используя асинхронные API.

    Видимо #1 вас был раньше?


    This posting is provided "AS IS" with no warranties, and confers no rights.

    segunda-feira, 22 de novembro de 2021 22:12
    Moderador
  • Проблема оказалась совсем не в том месте, где я искал. Она появилась из-за того, что ранее при загрузке файла мы ждали callback (совершенно верно. 1 вариант был раньше). А теперь нужно ждать функцию в отдельном потоке. В результате получается поток в потоке и все повисает. Приведу пример:

    Пусть мы загружаем изображения. Программа в отдельном потоке загружает интерфейс, создает элементы изображений и вызывает процедуру загрузки изображения в них. Сделаем это через кнопку.

        Private Sub Im_Click(sender As Object, e As RoutedEventArgs) Handles Im.Click
            TestLStack.Children.Clear()
    
            Threading.ThreadPool.QueueUserWorkItem(
                New Threading.WaitCallback(
                Sub()
                    Dim tmpI As ImageEl
                    For i = 0 To 49
                        Dispatcher.Invoke(Sub()
                                              tmpI = New ImageEl
                                              TestLStack.Children.Add(tmpI)
                                          End Sub)
                        tmpI.LoadImage("ПУТЬ НА ДИСКЕ")
                    Next
                End Sub))
        End Sub

    Загружаем изображение с диска

        Public Sub LoadImage(PathToFile As String)
    	'Создается новый поток, так как есть варианты вызова этой процедуры в основном потоке 
            Threading.ThreadPool.QueueUserWorkItem(
               New Threading.WaitCallback(
            Sub()
    	    'Вместо загрузки на сервере мы проверим файл по пути на диске
                If FileIO.FileSystem.FileExists(PathToFile) Then
    
    		Dim BitmapImage = New BitmapImage
                    Using Bs = New IO.FileStream(PathToFile, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.Read)
                        BitmapImage.BeginInit()
                        BitmapImage.CacheOption = BitmapCacheOption.OnLoad
                        BitmapImage.StreamSource = Bs
                        If Resize <> 0 Then BitmapImage.DecodePixelWidth = Resize
                        BitmapImage.EndInit()
                    End Using
                    BitmapImage.Freeze()
            
                    Dispatcher.Invoke(Sub() myImage.Source = BitmapImage)               
                End If
            End Sub))
        End Sub

    Все начинает работать нормально, если я уберу Threading.ThreadPool.QueueUserWorkItem(New Threading.WaitCallback(Sub() в процедуре LoadImage. Если же ставлю как тут - даже локальные файлы загружаются с долгим зависанием. А потом второй раз уже быстро. Ранее я проблем с потоком внутри потока не наблюдал. Почему так происходит?



    • Editado Siompc terça-feira, 23 de novembro de 2021 17:45
    terça-feira, 23 de novembro de 2021 17:43
  • Я так думаю это происходит потому что у вас стремительно кончаются потоки в пуле (по умолчанию 25) и как только это происходит очередной вызов Threading.ThreadPool.QueueUserWorkItem() блокируется до освобождения потока одного из потоков в пуле.


    This posting is provided "AS IS" with no warranties, and confers no rights.

    terça-feira, 23 de novembro de 2021 18:48
    Moderador
  • Печаль беда. Конечно потоков больше 25 будет. Так как при загрузке файла через функцию создается новый поток на каждую из них. А одновременно ожидать могут и 25 и 50 файлов.

    Есть какой-нибудь способ ожидания результатов выполнения работы функции без создания дополнительного потока? Или придется опять возвращать старый класс для загрузки и уже через callback отслеживать завершение загрузки...

    terça-feira, 23 de novembro de 2021 19:03
  • Печаль беда. Конечно потоков больше 25 будет. Так как при загрузке файла через функцию создается новый поток на каждую из них. А одновременно ожидать могут и 25 и 50 файлов.

    Есть какой-нибудь способ ожидания результатов выполнения работы функции без создания дополнительного потока? Или придется опять возвращать старый класс для загрузки и уже через callback отслеживать завершение загрузки...

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

    Можете так же увеличить число потоков в пуле или ограничить число одновременно используемых потоков. Последнее более разумно так как очевидно пропускная способность сети конечна и общая скорость загрузки будет максимальной при определенном (и сравнительно небольшом) числе одновременных загрузок.

    Реализация второго подхода не сложна. Надо лишь создать очередь загрузок (например используя класс Queue<T>) и при исчерпании потоков ставить загрузки в очередь вместо немедленной попытки исполнения. Потоки после завершения загрузки будет проверять есть ли что либо в очереди и если есть то снимать очередную загрузку из очереди и выполнять ее. Это будет продолжаться до тех пор пока очередь не опустеет.


    This posting is provided "AS IS" with no warranties, and confers no rights.

    • Marcado como Resposta Siompc terça-feira, 23 de novembro de 2021 19:47
    terça-feira, 23 de novembro de 2021 19:22
    Moderador