J'ai un peu de difficulté à sécuriser le chargement et le filtrage de mes données.Aidez-moi à sécuriser ce thread de code
Le code suivant sur la classe de base de mon contrôle qui gère toute la population de données via un BackgroundWorker. Cela tend à jeter l'erreur sur "this.DataWorker.RunWorkerAsync()" en disant que BackgroundWorker est occupé.
/// <summary>
/// Handles the population of the form data.
/// </summary>
/// <param name="reload">Whether to pull data back from the WebService.</param>
public void Populate(bool reload)
{
if (!this.DataWorker.IsBusy)
{
// Disable the filter options
IvdSession.Instance.FilterManager.SetEnabledState(this.GetType(), false);
// Perform the population
this.DataWorker.RunWorkerAsync(reload);
}
else if (!reload)
{
// If the data worker is busy and this is a not reload, then something bad has happened (i.e. the filter has run during a reload.)
throw new InvalidOperationException("The DataWorker was busy whilst asked to reload.");
}
}
Le code est appelé dans deux places possibles. Tout d'abord par une minuterie sur la forme que le contrôle est:
private void tmrAutoRefresh_Tick(object sender, EventArgs e)
{
if (!(this.CurrentBody == null))
{
this.CurrentBody.Populate(true);
}
}
Et d'autre part, chaque fois qu'un utilisateur sélectionne une option de filtre à partir d'un certain nombre de listes déroulantes:
public void Filter()
{
if (!m_BlockFilter)
{
IvdInstance.Main.CurrentBody.FirstRun = true;
IvdInstance.Main.CurrentBody.Populate(false);
}
}
La minuterie sur la La forme principale s'exécute toutes les 60 secondes et passe à la méthode Populate. En passant reload comme trues dit le BackgroundWorker qu'il doit tirer vers le bas une nouvelle série de données de la WebService:
void dataWorker_DoWork(object sender, DoWorkEventArgs e)
{
try
{
if (base.FirstRun)
{
base.CleanListView();
}
if ((bool)e.Argument)
{
byte[] serialized = IvdSession.DataAccess.GetServiceCalls(IvdSession.Instance.Company.Description, IvdSession.Instance.Company.Password, null);
m_DataCollection = new DalCollection<ServiceCallEntity>(serialized);
}
List<ServiceCallEntity> collection = this.ApplyFilter();
base.HandlePopulation<ServiceCallEntity>(collection, e);
}
catch (WebException ex)
{
// Ignore - Thrown when user clicks cancel
}
catch (System.Web.Services.Protocols.SoapException ex)
{
// Log error on server and stay transparent to user
base.LogError(ex);
}
catch (System.Data.SqlClient.SqlException ex)
{
// Inform user that the database is unavailable
base.HandleSystemUnavailable(ex);
}
}
Pour autant que je sache, l'erreur se produit lorsque je parviens à cliquer sur une option de filtre exactement en même temps, le temporisateur déclenche l'événement de population. Je suppose qu'il manque quelque chose à la méthode Populate, c'est-à-dire un verrou, mais je ne suis pas sûr de savoir comment l'utiliser correctement dans ce cas.
Le code est préféré vers l'entrée de l'utilisateur. Si un utilisateur sélectionne une option de filtre, la mise à jour automatique doit être bloquée. Si la mise à jour automatique se déclenche, les options de filtre sont temporairement désactivées. S'ils tirent en même temps, l'entrée de l'utilisateur devrait être prioritaire (si possible).
Espérons que quelqu'un peut vous aider!
@Groo, ne connais pas la spécification exacte de Windows, mais avec des multi-cœurs, ne seriez-vous pas en mesure d'avoir deux choses en cours d'exécution simultanément? –
Oui, la méthode * devrait * être thread-safe. Mais il existe une règle selon laquelle vous devez toujours mettre à jour les éléments Gui depuis le thread Gui. Donc, Windows.Forms.Timer prend soin d'ajouter le gestionnaire d'événements à la file d'attente de threads Gui (il l'appelle depuis le thread Gui), pour simplifier les choses. – Groo
Hey, vous plaisantez avec "ne sais pas la spécification exacte de Windows"? :) – Groo