programacion - ¿Por qué el multihilo en C#no alcanza el 100% de la CPU?
programacion de hilos c# (13)
¿Estás seguro de que tus tareas requieren una actividad intensa del procesador? ¿Hay algún procesamiento de IO? Esta puede ser la razón de su 50% de carga.
Prueba: intente usar solo 2 hilos y establezca la afinidad de cada hilo para cada núcleo. A continuación, abra el administrador de tareas y observe la carga de ambos núcleos.
Estoy trabajando en un programa que procesa muchas solicitudes, ninguna de las cuales alcanza más del 50% de la CPU ( actualmente estoy trabajando en un núcleo doble ). Así que creé un hilo para cada solicitud, todo el proceso es más rápido. Procesando 9 solicitudes, un solo hilo dura 02min08s, mientras que con 3 hilos trabajando simultáneamente el tiempo disminuyó a 01min37s, pero no mantiene el 100% de CPU, solo alrededor del 50%.
¿Cómo podría permitir que mi programa utilizara la capacidad de procesadores completos?
EDITAR La aplicación no está IO o la memoria está limitada, están en niveles razonables todo el tiempo.
Creo que tiene algo que ver con lo de "doble núcleo".
Hay una invocación de método bloqueado que utiliza cada solicitud, pero es muy rápido, no creo que este sea el problema.
La parte más costosa de mi código es la llamada de un dll a través de COM (el mismo método externo se llama desde todos los hilos). Este dll tampoco tiene memoria o está limitado por IO, es un componente de reconocimiento AI, estoy haciendo un reconocimiento OCR de cheques de pago, un cheque de pago por solicitud.
EDIT2
Es muy probable que el método STA COM sea mi problema. Me puse en contacto con los propietarios de los componentes para resolver este problema.
¿Tiene bloqueo significativo dentro de su aplicación? Si los hilos están esperando mucho el uno al otro, eso podría explicarlo fácilmente.
Aparte de eso (y las otras respuestas dadas), es muy difícil de adivinar, realmente. Un perfilador es tu amigo ...
EDITAR: Bien, dado los comentarios a continuación, creo que estamos en algo:
La parte más costosa de mi código es la llamada de un dll a través de COM (el mismo método externo se llama desde todos los hilos).
¿Se está ejecutando el método COM en una STA por casualidad? De ser así, solo usará un hilo y serializará las llamadas. Sospecho fuertemente que esa es la clave. Es similar a tener un bloqueo alrededor de esa llamada al método (no exactamente lo mismo, ciertamente).
Así que resolvió el problema de usar un único objeto COM y ahora tiene un problema de E / S.
El mayor tiempo de ejecución para múltiples hilos probablemente se debe a la mezcla de IO aleatorio juntos, lo que lo desacelerará todo.
Si el conjunto de datos encajará en la RAM, intente ver si puede captarlo previamente en caché. Tal vez simplemente leyendo los datos, o tal vez la asignación de memoria junto con un comando para que esté disponible.
Esta es la razón por la que las bases de datos SQL a menudo elegirán el escaneo de tabla secuencial sobre un escaneo de índice en consultas que no esperarías: puede ser mucho más rápido leer todo en orden que leerlo en fragmentos aleatorios.
Depende de lo que haga su programa: el trabajo realizado por sus Solicitudes simultáneas podría estar limitado por IO, limitado por la velocidad de (por ejemplo) su disco duro, en lugar de por CPU, cuando vería su CPU llegar al 100%.
Después de la edición, suena como COM STA objetos podría ser el culpable.
¿Todos los hilos llaman a la misma instancia del objeto COM? ¿Sería posible hacer que su trabajador rosca STA hilos, y crear una instancia separada del objeto COM en cada hilo. De esta forma, es posible evitar el cuello de botella de STA.
Para decir si un COM coclass es STA:
class Test
{
static void Main() //This will be an MTA thread by default
{
var o = new COMObjectClass();
// Did a new thread pop into existence when that line was executed?
// If so, .NET created an STA thread for it to live in.
}
}
En realidad, esta no es una respuesta, pero ¿ha revisado perfmon para ver qué recursos está usando y ha ejecutado profilers en el código para ver dónde está pasando el tiempo?
¿Cómo determinó que IO u otros recursos que no sean CPU no sean el cuello de botella?
¿Puedes dar una breve descripción de lo que están haciendo los hilos?
Parece que el rendimiento de su aplicación puede no estar ''limitado'' por la cantidad de recursos de CPU disponibles. Si está procesando solicitudes a través de la red, la (s) CPU (s) pueden esperar que lleguen los datos o que el dispositivo de red transfiera los datos. Alternativamente, si necesita buscar datos para cumplir con la solicitud, la CPU puede estar esperando el disco.
Tal vez estoy malinterpretando algo, pero dijiste que ninguna de tus solicitudes (cada una en un hilo separado) alcanza el 100% de la CPU.
Qué sistema operativo estás usando?
Me parece recordar vagamente que en las versiones anteriores de Windows (por ejemplo, principios de XP y 2000), la utilización de CPU se consideraba de un total de dos procesadores, por lo que un solo subproceso no podía superar el 50% a menos que fuera el proceso inactivo. ..
si su proceso se está ejecutando en la CPU 0 y está generando hilos allí, el máximo que alcanzará será del 50%. Vea si tiene subprocesos ejecutándose en ambos núcleos o en solo uno. Me aventuraría a adivinar que estás aislado en un solo núcleo, o que uno de tus recursos dependientes está bloqueado en un único núcleo. Si alcanza exactamente el 50%, es muy probable que un solo núcleo sea su cuello de botella.
Probablemente ya no sea el procesador el cuello de botella para completar su proceso. Es probable que el cuello de botella se haya trasladado al acceso al disco, acceso a la red o acceso a la memoria. También podría tener una situación en la que sus hilos compitan por cerraduras.
Solo usted sabe exactamente lo que están haciendo sus hilos, por lo que debe mirarlos teniendo en cuenta lo anterior.
Una nota más, ¿ha intentado ejecutar su código no desde Visual Studio (independientemente de las configuraciones de versión / depuración)?
El problema es el objeto COM. Es STA, y tampoco puedo tener dos instancias ejecutándose simultáneamente en el mismo proceso. Cuando creo una instancia para la clase COM, la otra se vuelve inutilizable.
Me he puesto en contacto con los desarrolladores de componentes, están pensando en lo que pueden hacer por mí.
Gracias a todos ;)
El problema es el objeto COM.
La mayoría de los objetos COM se ejecutan en el contexto de un "apartamento de subproceso único". (Es posible que haya visto una anotación [STAThread] en el método principal de una aplicación .NET de vez en cuando).
Efectivamente, esto significa que todos los envíos a ese objeto se manejan con un solo hilo. Lanzar más núcleos al problema solo te brinda más recursos que pueden sentarse y esperar o hacer otras cosas en .NET.
Es posible que desee echar un vistazo a este artículo de Joe Duffy (el jefe de .NET tipo paralelo en Microsoft) sobre el tema.
http://www.bluebytesoftware.com/blog/PermaLink,guid,8c2fed10-75b2-416b-aabc-c18ce8fe2ed4.aspx
En la práctica, si tiene que hacer muchas cosas contra un único objeto COM como este, se le aplicará una manguera, porque .NET simplemente serializará los patrones de acceso internamente a sus espaldas. Si puede crear múltiples objetos COM y usarlos, puede resolver el problema porque cada uno se puede crear y acceder desde un hilo STA distinto. Esto funcionará hasta que llegue a aproximadamente 100 subprocesos STA, entonces las cosas irán mal. Para más detalles, mira el artículo.
Creo que tuve un problema similar. Estaba creando varios hilos en c # que ejecutaban código c ++ a través de una interfaz COM. Mi CPU de doble núcleo nunca llegó al 100%.
Después de leer esta publicación, casi me rindo. Luego intenté llamar a SetApartmentState (ApartmentState.STA) en mis hilos.
Después de solo cambiar esto, la CPU se agotó al máximo.