C# Problème de consommation sur le processeur

BlueDream

Administrateur
Membre du personnel
Inscrit
8 Decembre 2012
Messages
2 010
Reactions
149
#1
Bonsoir,

je demande votre aide ce soir car je suis face à un problème de mémoire incompréhensible.
Je possédais mon propre dispatcher au par avant mais il consommait beaucoup trop donc j'ai opté pour le Dispatcher de BiM bien plus optimisé.
Je l'ai un peu modifié, le voici:

ClientDispatcher.cs
Code:
public class ClientDispatcher
    {

        private SortedDictionary<MessagePriority, Queue<Tuple<NetworkMessage, object>>> messagesToDispatch = new SortedDictionary<MessagePriority, Queue<Tuple<NetworkMessage, object>>>();

        private int currentThreadId;
        private object currentProcessor;

        public object CurrentProcessor
        {
            get { return currentProcessor; }
        }

        public int CurrentThreadId
        {
            get { return currentThreadId; }
        }

        private ManualResetEventSlim resumeEvent = new ManualResetEventSlim(true);
        private ManualResetEventSlim messageEnqueuedEvent = new ManualResetEventSlim(false);

        private bool stopped;
        private bool dispatching;

        public event Action<ClientDispatcher, NetworkMessage> MessageDispatched;

        protected void OnMessageDispatched(NetworkMessage message)
        {
            var evnt = MessageDispatched;
            if (evnt != null)
                MessageDispatched(this, message);
        }

        public ClientDispatcher()
        {
            foreach (var value in Enum.GetValues(typeof(MessagePriority)))
            {
                messagesToDispatch.Add((MessagePriority)value, new Queue<Tuple<NetworkMessage, object>>());
            }
        }

        public bool Stopped
        {
            get { return stopped; }
        }

        public void Enqueue(NetworkMessage message, bool executeIfCan = true)
        {
            Enqueue(message, null, executeIfCan);
        }

        public virtual void Enqueue(NetworkMessage message, object token, bool executeIfCan = true)
        {
            if (executeIfCan && IsInDispatchingContext())
            {
                Dispatch(message, token);
            }
            else
            {
                lock (messageEnqueuedEvent)
                {
                    messagesToDispatch[MessagePriority.Normal].Enqueue(Tuple.Create(message, token));

                    if (!dispatching)
                        messageEnqueuedEvent.Set();
                }
            }
        }

        public bool IsInDispatchingContext()
        {
            return Thread.CurrentThread.ManagedThreadId == currentThreadId &&
                 currentProcessor != null;
        }

        public void ProcessDispatching(object processor)
        {
            if (stopped)
                return;

            if (Interlocked.CompareExchange(ref currentThreadId, Thread.CurrentThread.ManagedThreadId, 0) == 0)
            {
                currentProcessor = processor;
                dispatching = true;

                var copy = messagesToDispatch.ToArray();
                foreach (var keyPair in copy)
                {
                    if (stopped)
                        break;

                    while (keyPair.Value.Count != 0)
                    {
                        if (stopped)
                            break;

                        var message = keyPair.Value.Dequeue();

                        if (message != null)
                            Dispatch(message.Item1, message.Item2);
                    }
                }

                currentProcessor = null;
                dispatching = false;
                Interlocked.Exchange(ref currentThreadId, 0);
            }

            lock (messagesToDispatch)
            {
                if (messagesToDispatch.Sum(x => x.Value.Count) > 0)
                    messageEnqueuedEvent.Set();
                else
                    messageEnqueuedEvent.Reset();
            }
        }

        protected virtual void Dispatch(NetworkMessage message, object token)
        {
            MethodInfo method = FrameManager.GetMethodByName(message.ToString());

            if (method == null)
                return;

            if (method != null)
            {
                object[] parameters = new object[] { token, message };
                method.Invoke(null, parameters);
            }

            OnMessageDispatched(message);
        }

        /// <summary>
        /// Block the current thread until a message is enqueued
        /// </summary>
        public void Wait()
        {
            if (stopped)
                resumeEvent.Wait();

            if (messagesToDispatch.Sum(x => x.Value.Count) > 0)
                return;

            messageEnqueuedEvent.Wait();
        }

        public void Resume()
        {
            if (!stopped)
                return;

            stopped = false;
            resumeEvent.Set();
        }

        public void Stop()
        {
            if (stopped)
                return;

            stopped = true;
            resumeEvent.Reset();
        }

        public void Dispose()
        {
            Stop();

            foreach (var messages in messagesToDispatch)
            {
                messages.Value.Clear();
            }
        }

        private Stopwatch _spy;

        /// <summary>
        /// Says how many milliseconds elapsed since last message. 
        /// </summary>
        public long DelayFromLastMessage
        {
            get
            {
                if (_spy == null) _spy = Stopwatch.StartNew(); return _spy.ElapsedMilliseconds;
            }
        }

        /// <summary>
        /// Reset timer for last received message
        /// </summary>
        protected void ActivityUpdate()
        {
            if (_spy == null)
                _spy = Stopwatch.StartNew();
            else
                _spy.Restart();
        }

    }

    public enum MessagePriority
    {
        VeryHigh = 4,
        High = 3,
        Normal = 2,
        Low = 1,
        VeryLow = 0,
    }

La classe qui gère le ClientDispatcher:

DispatcherTask.cs
Code:
public class DispatcherTask
    {
        public ClientDispatcher Dispatcher { get; private set; }

        public bool Running
        {
            get;
            private set;
        }

        public object Processor { get; set; }

        public DispatcherTask(ClientDispatcher dispatcher)
        {
            Dispatcher = dispatcher;
            Processor = this;
        }

        public DispatcherTask(ClientDispatcher dispatcher, object processor)
        {
            Dispatcher = dispatcher;
            Processor = processor;
        }

        public void Start()
        {
            Running = true;
            Task.Factory.StartNew(Process);
        }

        public void Stop()
        {
            Running = false;
        }

        private void Process()
        {
            while (Running)
            {
                Dispatcher.Wait();

                if (Running)
                    Dispatcher.ProcessDispatching(Processor);
            }
        }
    }

Et actuellement au lancement mon Bot consomme 0% sur le processeur, 100 000K de mémoire et 16 Threads.

Puis a la connexion tout reste stable mais la mémoire monte lentement.
Au bout de 5 minutes la mémoire continue a grimper et mon bot passe à 40% / 50% d'utilisation du processeur.
Je l'ai laissé tourner une heure pour voir, je suis arrivé à 800 000K pour un seul compte, 40/50 Threads, mon ventilo geulait et je pouvait même plus bouger les fenêtres enfants tellement sa bugait.

Je me déconnecte et rien ne change, incompréhensible, cela s'empire si on multiplie le nombres de comptes.
Je ne m'en rendait pas compte puisque mes tests a chaque connexion ne durent pas plus de 5mins donc je ne ressentais aucun lag sur les fenêtres.

J'ai essayé de supprimer l'interface et ma minimap mais cela n'a rien changé. Sa commence a buger au moment ou on connecte un compte.
Je doute que cela vienne du Dispatcher puisque il est stoppé a la déconnexion, mais c'est la seul chose qui tourne sur le bot pendant ces montées de mémoires.

Si quelqu'un pouvait m'aider :ugeek:

Bonne soirée
 

Sorrow

Membre Actif
Inscrit
5 Mai 2012
Messages
376
Reactions
26
#2
Ya pas de profiler de mémoire en C# ?
Visiblement tu garde une variable (ou un vecteur) qui pointe sur quelques chose qui contient pas mal de donné et de ce fait le garbage collector ne prend pas en compte ses données là vue que pour lui tu compte t'en servir plustard.
 

BlueDream

Administrateur
Membre du personnel
Inscrit
8 Decembre 2012
Messages
2 010
Reactions
149
#3
Je ne vois pas du tout ce que tu veux dire par profiler de mémoire mais oui j'ai des variables assez lourdes, par exemple ya pas mal de données que je stock en local et que je sérialise dans un gros dictionnaire.

Cela pourrait venir de ce genre de choses ?

EDIT: Voila 1 heure que je suis sur ce problème et la je viens de debug et je n'ai plus de problème. Je ne sais même pas ce que j'ai fait...
Je vais voir sur le long terme, je vous tiens au courant.
 

Sorrow

Membre Actif
Inscrit
5 Mai 2012
Messages
376
Reactions
26
#4

BlueDream

Administrateur
Membre du personnel
Inscrit
8 Decembre 2012
Messages
2 010
Reactions
149
#6
@Sorrow Merci bien

@Sparkdaemon Non j'ai vraiment fait gaffe a mon code et la le problème est résolu par je ne sais quel Miracle.
Le fantôme de Maxou1012 ?

EDIT:



3 Bots récoltent.
 

Sparkdaemon

Staff
Membre du personnel
Inscrit
7 Avril 2009
Messages
556
Reactions
3
#7
L'un des thread devait être bloqué dans une boucle infinie.
 

BlueDream

Administrateur
Membre du personnel
Inscrit
8 Decembre 2012
Messages
2 010
Reactions
149
#8
J'avais mis des points d'arret, aucun dispatcher ne tournait.
 

Kyu

Staff
Membre du personnel
Inscrit
4 Octobre 2009
Messages
327
Reactions
8
#9
Avec un profiler, tu peux avoir l'état des threads, leur consomation, voir ce qu'ils executent,


avoir une vue détailler de la mémoire décomposer en primitive/objet,


et les différentes mémoire gérer par le GC,


ainsi que le temps d'execution des méthodes.
 

BlueDream

Administrateur
Membre du personnel
Inscrit
8 Decembre 2012
Messages
2 010
Reactions
149
#10
Ah oui c'est très avancé, merci de votre aide.
 

BlueDream

Administrateur
Membre du personnel
Inscrit
8 Decembre 2012
Messages
2 010
Reactions
149
#11
Donc problème résolu, ca vient de mon timer que j'avais complètement oublié mais comment un simple timer peut-il faire autant monter le process ?
J'effectue une première connexion à mon serveur, si sa foire j'informe et je met un timer toutes les 10s pour retenter de se connecter.

Code:
using AmaknaCore.Common.IO;
using AmaknaCore.Common.Network;
using AmaknaCore.Protocol;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;

namespace AmaknaCore.Debug.Managers
{
    public static class ServerManager
    {

        #region Variables

        public static SimpleClient Network;

        public static bool IsConnected;

        private static Timer ReconnectTimer;

        public static bool Running;

        private static bool Silent;

        #endregion

        #region Builder

        static ServerManager()
        {
            Network = new SimpleClient();

            Network.Connected += Connected;
            Network.Disconnected += Disconnected;
            Network.DataReceived += DataReceived;
            Network.Error += SocketError;

            IsConnected = false;
            Running = false;
            Silent = false;
        }

        #endregion

        #region Methods

        public static void OpenConnection()
        {
            if (Running == true)
                return;

            Network.Start(Configuration.ServerAddress, Configuration.ServerPort);
            Running = true;
        }

        public static void CloseConnection()
        {
            if (Running == false)
                return;

            Network.Stop();
            Running = false;
        }

        private static void TryReconnect()
        {
            Silent = true;
            ReconnectTimer = new Timer(10000);
            ReconnectTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
            ReconnectTimer.Enabled = true;
            ReconnectTimer.Start();
        }

        #endregion

        #region Events

        private static void OnTimedEvent(object source, ElapsedEventArgs e)
        {
            if (IsConnected == true)
            {
                ReconnectTimer.Enabled = false;
                ReconnectTimer.Stop();
                Silent = false;
                return;
            }

            Network.Start(Configuration.ServerAddress, Configuration.ServerPort);
        }

        private static void Connected(object sender, SimpleClient.ConnectedEventArgs e)
        {
            IsConnected = true;
            ConsoleManager.Logger.Info("Connected with Amakna Server.");
        }

        private static void Disconnected(object sender, SimpleClient.DisconnectedEventArgs e)
        {
            IsConnected = false;
            ConsoleManager.Logger.Warning("Connection lost with server.");
            TryReconnect();
        }

        private static void DataReceived(object sender, SimpleClient.DataReceivedEventArgs e)
        {
            var messageDataReader = new BigEndianReader(e.Data.Data);
            NetworkMessage message = MessageReceiver.BuildMessage((uint)e.Data.MessageId.Value, messageDataReader);
        }

        private static void SocketError(object sender, SimpleClient.ErrorEventArgs e)
        {
            if (Silent == false)
                ConsoleManager.Logger.Warning("Impossible de se connecter au serveur.");
            IsConnected = false;
            TryReconnect();
        }

        #endregion

    }
}
 

Kyu

Staff
Membre du personnel
Inscrit
4 Octobre 2009
Messages
327
Reactions
8
#12
Code:
if (IsConnected == true)
{
    ReconnectTimer.Enabled = false;
    ReconnectTimer.Stop();
    Silent = false;
    return;
}
Ton timer n'est jamais arrété, et la tentative de reconnexion en créer un nouveau, tu as donc une boucle de création de timers qui continue de s’exécuter et produit de nouveaux timers, ce qui conduit à un overflow.
 

BlueDream

Administrateur
Membre du personnel
Inscrit
8 Decembre 2012
Messages
2 010
Reactions
149
#13
Sauf que durant mes tests, le client n'est jamais connecté au serveur, donc il ne devrait y avoir qu'un seul timer qui tourne, malgré mon erreur.
 

Kyu

Staff
Membre du personnel
Inscrit
4 Octobre 2009
Messages
327
Reactions
8
#14
TryReconnect() n'est jamais appelé avec IsConnected valant true, donc ton timer n'est jamais modifié, il continue de s'éxecuter et dans la contuinité, OnTimedEvent(), créer une nouvelle instance de timer. Boucle infini.
Met une print et tu devrais le voir apparaître assez souvent de manière exponentielle.
 

BlueDream

Administrateur
Membre du personnel
Inscrit
8 Decembre 2012
Messages
2 010
Reactions
149
#15
Merci beaucoup de votre aide.
 
Haut Bas