Bonjour.

J'ai une application multi-threadée dont chaque thread a besoin de lancer un programme externe pour générer une image.

Les performances n'étant pas terrible, j'ai décidé de passer par un pool de Process pour économiser les temps de lancement de chaque process.
Seulement voilà, mes performances sont pire qu'avant

plutôt que du pseudo code je vous propose ma classe (un peu longue).
L'idée de base c'est qu'elle gère tout en interne rendant transparent au reste de l'application l'utilisation de plusieurs Process.
Le pool de procesus contient, à l'instanciation de la classe, un certain nombre de Process démarrés qui sont en attente de paramètre sur leur std input.

Quand un process a fini, il ecrit "[end]" sur sdt output.

j'utilise une petite classe interne pour gérer les infos d'attente du process et d'état (occupé ou non)

J'ai donc plusieurs questions :
- Est-ce que mon implémentation tient la route ?
- Est-ce normal que les performances soient dégradée comparer à une solution où chaque thread instancie, initialise et clôture le Process ?

merci
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
 
using System.Diagnostics;
 
using System.Runtime.CompilerServices;
using System.Threading;
 
namespace MapnikWrapperTester
{
    /// <summary>
    /// process pool Class
    /// </summary>
    public class Pool_process
    {
        // Size of the pool, defines how many processes will be started
        private const int POOL_SIZE = 5;
 
        // singelton
        private static Pool_process _unique;
 
        // list of instanciated process
        private List<Couple> processList;
        // number to iterate images
        private uint _tileNum;
 
        // tell if the class is being disposed
        private bool _isDisposing;
 
        // constructor
        private Pool_process()
        {
            processList = new List<Couple>();
            for (int i = 0; i < POOL_SIZE; i++)
            {
                // loads as many process as needed
                processList.Add(new Couple(newProcess(true)));
            }
 
            _tileNum = 0;
            _isDisposing = false;
        }
 
        /// <summary>
        /// pick a free process and execute the command
        /// </summary>
        /// <param name="args">command aruments</param>
        /// <returns>result of process</returns>
        public Image execute(string args)
        {
            Couple cp = getFreeProc(); // try to pick a process from the pool
            Image img;
            // image path
            string map_res_path = @"C:\temp\map_out\map" + _tileNum++ + ".png";
            // get a free proc
            if (cp != null)
            {
                Process proc = cp.Proc;
                cp.AutoEvent = new AutoResetEvent(false);
                proc.StandardInput.WriteLine(args + " -O \"" + map_res_path + "\" go");
 
                // wait for the map has been done (see Pool_Process.on_proc_output(...) )
                cp.AutoEvent.WaitOne();
 
                proc.StandardInput.WriteLine("1"); // don't stop the process;
            }
            else // if no free Process available, creates a new one
            {
                Console.WriteLine("Pool is fully used, creating a new process");
                Process proc = newProcess(false);
 
                proc.StandardInput.WriteLine(args + " -O \"" + map_res_path + "\" go 0"); // add "0" to tell the process to exit
 
                proc.WaitForExit();
                proc.Close();
            }
 
            // map is done
            img = Image.FromFile(map_res_path);
 
            return img;
        }
 
        // get a free process in the list. returns null if all processes in use
        [MethodImpl(MethodImplOptions.Synchronized)]
        private Couple getFreeProc()
        {
            for(int i=0 ; i<processList.Count ; i++)
                if (processList[i].IsUsed == false)
                {
                    processList[i].IsUsed = true;
                    return processList[i];
                }
            return null;
        }
 
        /// <summary>
        /// get unique instance of the Class
        /// </summary>
        /// <returns>Pool_process</returns>
        public static Pool_process getInstance()
        {
            if (_unique == null)
                _unique = new Pool_process();
            return _unique;
        }
 
        // triggered when process in pool is exiting
        private void on_proc_Exit(Object sender, EventArgs e)
        {
            if (sender is Process)
            {
                if (((Process)sender).ExitCode < 0 && _isDisposing == false)
                {
                    for (int i = 0; i < processList.Count; i++)
                        if (processList[i].Proc == (Process)sender)
                            processList[i].Proc = newProcess(true);
                    throw new SystemException("Un processus a échoué. code : " + ((Process)sender).ExitCode + "(" + ((Process)sender).StandardError.ReadToEnd() + ")");
                }
            }
        }
 
        // triggered when process writes on stdout.
        private void on_proc_output(object sender, DataReceivedEventArgs e)
        {
            if (!String.IsNullOrEmpty(e.Data))
            {
                if(e.Data == "[end]")
                {
                    foreach(Couple c in processList)
                    {
                        if(c.Proc == (Process)sender)
                        {
                            // resumes calling thread
                            c.AutoEvent.Set();
                            // release process
                            c.IsUsed = false;
                        }
                    }
                }
            }
        }
 
        // creates a new process and start it. Process will then be idling for input.
        private Process newProcess(bool is_pool)
        {
            Process proc = new Process();
            // hide window
            proc.StartInfo.UseShellExecute = false;
            //proc.StartInfo.CreateNoWindow = true;
            //proc.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
            proc.StartInfo.RedirectStandardOutput = true;
            proc.StartInfo.RedirectStandardError = true;
            proc.StartInfo.RedirectStandardInput = true;
            proc.StartInfo.StandardOutputEncoding = System.Text.Encoding.UTF7;
            //proc.StartInfo.WorkingDirectory = GlobalDatas.Configuration.Path["map_builder"];
            //proc.StartInfo.FileName = GlobalDatas.Configuration.Path["map_builder"] + "Map Builder.exe";
            proc.StartInfo.WorkingDirectory = @"C:\Work\VStudio\Map Builder\release";
            proc.StartInfo.FileName = @"C:\Work\VStudio\Map Builder\release\Map Builder.exe";
            if(is_pool)
                proc.Exited += new EventHandler(on_proc_Exit);
            proc.OutputDataReceived += new DataReceivedEventHandler(on_proc_output);
            proc.EnableRaisingEvents = true;
 
            proc.Start();
 
            proc.BeginOutputReadLine();
 
            return proc;
        }
 
        // disposing the calss, kill the processes.
        public void Dispose()
        {
            _isDisposing = true;
            foreach (Couple c in processList)
            {
                c.Proc.Kill();
                c.Proc.Close();
            }
            Console.WriteLine("all process killed");
        }
 
 
        /// <summary>
        /// Util class to handle process and their status
        /// </summary>
        private class Couple
        {
            private Process _proc;
            private bool _isUsed;
            private AutoResetEvent _autoEvent;
            public Couple(Process p)
            {
                _proc = p;
                _isUsed = false;
            }
 
            public Process Proc
            {
                get { return _proc; }
                set { _proc = value; }
            }
 
            public bool IsUsed
            {
                get { return _isUsed; }
                set { _isUsed = value; }
            }
 
            public AutoResetEvent AutoEvent
            {
                get { return _autoEvent; }
                set { _autoEvent = value; }
            }
        }
    }
}