Pregunta

Tengo un objeto de transmisión y estoy usando BeginRead para comenzar a leer (obviamente) en un búfer; La función AsyncCallback se llama una vez que se completa la lectura. Dentro de esta función, puedo verificar si el usuario desea obtener el siguiente 'bloque' e iniciar el proceso BeginRead nuevamente.

El problema que tengo es que el usuario puede optar por cancelar mientras la transmisión aún está leyendo (por lo que antes de que se llame a la función AsyncCallback), entonces, ¿cómo puedo cancelar la lectura de la transmisión?

Solo para explicar más a fondo el problema: parece que tendría el mismo resultado si uso un trabajador de fondo con el método de lectura de transmisión o el método asincrónico de BeginRead. El usuario podría quedarse esperando cualquier período de tiempo para que el método Read/BeginRead se complete antes de que pueda verificar si la transmisión deja de leer.

Editar: El código a continuación debería hacer el trabajo, estoy a un millón de millas de ser algo decente en C#, por lo que puede tener un par de errores, ya que dudo que sea perfecto, aunque demuestra la solución.

En resumen, el CworkManager administra un cierto número de hilos (que se mantienen dentro de una clase de CworkerDetail). Cada CworkerDetail tiene un estado, que puede ser expulsado, lo que significa que el trabajador puede ser iniciado, lo que significa que el trabajador está leyendo de una fuente, durante el cual el trabajador puede ser detenido al instante, ewriting, lo que guarda los datos que se leen al disco. Esto no se puede detener al instante y este proceso debe completarse antes de que el hilo se detenga. Finalmente, el gerente establece la eaborting si el trabajador debe ser abortado lo antes posible; En este momento, esto solo se establece si el trabajador está en medio de algo que no puede ser interrumpido (como escribir en el disco).

En este momento, en realidad no hay ninguna lectura o escritura en curso, ya que eso solo complicaría la solución principal (que es básicamente solo la función de parada que verifica un indicador de Cworker para ver si puede abortar instantáneamente); Como tal, simplemente hacemos que el hilo duerma.

El lado de la GUI es bastante simple con solo un cuadro de lista (que muestra el estado de cada trabajador) y un botón de detención y inicio. Todo el código está debajo, espero que esto ayude a alguien, pero como digo no soy brillante con C#, así que ten cuidado con los errores, etc.

Cworkmanager.cs:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;


namespace ThreadApplication {


    //A worker that spawns a number of threads (managed internally) that does nothing useful at all.
    public class CWorkManager {


        //The status of the worker.
        public enum EWorkerStatus {
            EWaiting,
            EReading,
            EWriting,
            EAborting,
        }


        //Holds all data relevant to the worker.
        private class CWorkerDetails {

            //Simple variables.
            private readonly Object _Lock=new Object();
            private Thread gThread;
            private EWorkerStatus gStatus;
            private CWorkManager gParentInstance;
            private int gIndex;

            //Simple constructor.
            public CWorkerDetails(int aIndex, CWorkManager aParentInstance, Thread aThread, EWorkerStatus aStatus) {
                gIndex=aIndex;
                gParentInstance=aParentInstance;
                gThread=aThread;
                gStatus=aStatus;
            }

            //Simple get set methods.
            public Thread GetThread() { lock(_Lock) { return gThread; } }
            public EWorkerStatus GetStatus() { lock(_Lock) { return gStatus; } }

            //Sets the status and automatically updates the GUI.
            public void SetStatus(EWorkerStatus aStatus) {
                lock(_Lock) {
                    gStatus=aStatus;
                    Form1.gInstance.Invoke(new UpdateGUIDelegate(gParentInstance.UpdateGUI), new object[] { gIndex, GetStatus() });
                }
            }

        }


        //Worker variable.
        private List<CWorkerDetails> gWorkers;


        //Simple constructor.
        public CWorkManager(int aWorkerCount){
            gWorkers=new List<CWorkerDetails>();
            for(int tIndex=0; tIndex<aWorkerCount; tIndex++)
                gWorkers.Add(null);
        }


        //Creates and starts the worker.
        public void StartWorker(int aWorkerIndex) {

            //Create a new worker if there is none or if it is waiting to start.
            if(gWorkers.ElementAt(aWorkerIndex)==null||gWorkers.ElementAt(aWorkerIndex).GetStatus()==EWorkerStatus.EWaiting)
                gWorkers[aWorkerIndex]=new CWorkerDetails(aWorkerIndex, this, new Thread(new ParameterizedThreadStart(WorkerMethod)), EWorkerStatus.EWaiting);

            //If the worker is waiting to start, then start.
            if(gWorkers.ElementAt(aWorkerIndex).GetStatus()==EWorkerStatus.EWaiting)
                gWorkers.ElementAt(aWorkerIndex).GetThread().Start(gWorkers.ElementAt(aWorkerIndex));
        }


        //Stops the worker.
        public void StopWorker(int aWorkerIndex) {

            //Do nothing if the worker is null.
            if(gWorkers.ElementAt(aWorkerIndex)==null)
                return;

            //Do nothing if the worker is waiting.
            if(gWorkers.ElementAt(aWorkerIndex).GetStatus()==EWorkerStatus.EWaiting)
                return;

            //If the worker is reading we can abort instantly.
            if(gWorkers[aWorkerIndex].GetStatus()==EWorkerStatus.EReading) {
                gWorkers[aWorkerIndex].GetThread().Abort();
                gWorkers[aWorkerIndex].SetStatus(EWorkerStatus.EWaiting);
                return;
            }

            //Since the worker is not reading or waiting, we have to request the 
            //worker to abort by itself.
            gWorkers[aWorkerIndex].SetStatus(EWorkerStatus.EAborting);

        }


        //Updates the GUI.
        private delegate void UpdateGUIDelegate(int aIndex, EWorkerStatus aStatus);
        private void UpdateGUI(int aIndex, EWorkerStatus aStatus) {
            Form1.gInstance.SetThreadStatus(aIndex, aStatus);
        }


        //This method is where all the real work happens.
        private void WorkerMethod(Object aWorker) {

            //Fetch worker.
            CWorkerDetails mWorker=(CWorkerDetails)aWorker;

            //Loop forever, the thread will exit itself when required.
            while(true) {

                //Is the worker status aborting - if so we stop here.
                if(mWorker.GetStatus()==EWorkerStatus.EAborting) {
                    mWorker.SetStatus(EWorkerStatus.EWaiting);
                    return;
                }

                //This would normally be reading from a stream which would cause the thread
                //to block, simulate this by just sleeping the thread.
                mWorker.SetStatus(EWorkerStatus.EReading);
                Thread.Sleep(3000);

                //Is the worker status aborting - if so we stop here.
                if(mWorker.GetStatus()==EWorkerStatus.EAborting) {
                    mWorker.SetStatus(EWorkerStatus.EWaiting);
                    return;
                }

                //All data has been read, set status to writing and again simulate by
                //sleeping the thread.
                mWorker.SetStatus(EWorkerStatus.EWriting);
                Thread.Sleep(3000);

            }

        }

    }


}

Form1.cs:


Contiene:

  • Un cuadro de lista (listbox_workerstatus)
  • Un botón (Button_Start)
  • Un botón (Button_stop)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;


namespace ThreadApplication {


    public partial class Form1:Form {


        public static Form1 gInstance;
        private CWorkManager gManager;


        public Form1() {
            InitializeComponent();
            Button_Start.Click+=new EventHandler(Button_Start_Click);
            Button_Stop.Click+=new EventHandler(Button_Stop_Click);
            gInstance=this;
            for(int tIndex=0; tIndex<5; tIndex++)
                ListBox_WorkerStatus.Items.Add("Created");
            gManager=new CWorkManager(ListBox_WorkerStatus.Items.Count);
        }


        public void SetThreadStatus(int aIndex, CWorkManager.EWorkerStatus aStatus) {
            ListBox_WorkerStatus.Items[aIndex]=aStatus.ToString();
        }


        private void Button_Start_Click(object sender, EventArgs e) {
            if(ListBox_WorkerStatus.SelectedIndex>=0) {
                gManager.StartWorker(ListBox_WorkerStatus.SelectedIndex);
            }
        }


        private void Button_Stop_Click(object sender, EventArgs e) {
            if(ListBox_WorkerStatus.SelectedIndex>=0) {
                gManager.StopWorker(ListBox_WorkerStatus.SelectedIndex);
            }
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e) {
            for(int tIndex=0; tIndex<ListBox_WorkerStatus.Items.Count; tIndex++) {
                gManager.StopWorker(tIndex);
            }
        }


    }


}

¿Fue útil?

Solución

Usar Backgroundworker

BackgroundWorker backgroundWorker1= new backgroundWorker()

private void InitializeBackgroundWorker()
{
        backgroundWorker1.DoWork += 
            new DoWorkEventHandler(backgroundWorker1_DoWork);

        backgroundWorker1.WorkerSupportsCancellation = true;
}



private void backgroundWorker1_DoWork(object sender, 
        DoWorkEventArgs e)
{   
        BackgroundWorker worker = sender as BackgroundWorker;

        e.Result = YourWorkToDo();
}

public void Start()
{
    backgroundWorker1.RunWorkerAsync()
}

public voic Cancel()
{
    backgroundWorker1.CancelAsync();
{

Si quieres más ayuda, deja un comentario

Otros consejos

Por favor, miraCancelar beginread este

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top