2015-12-23 36 views
6

Ho provato a implementare l'operazione di lettura lettura utilizzando le operazioni File IO e incapsulato in TransformBlock in modo da rendere queste operazioni thread-safe anziché utilizzare il meccanismo di blocco.Problema di memoria nell'implementazione di TPL Dataflow dell'operazione di scrittura lettura IO

Ma il problema è che quando provo a scrivere anche 5 file parallelamente c'è una memoria fuori eccezione e sull'utilizzo di questa implementazione sta bloccando il thread dell'interfaccia utente. L'implementazione viene eseguita nel progetto Windows Phone. Si prega di suggerire cosa c'è di sbagliato in questa implemantation.

File IO Operazione

public static readonly IsolatedStorageFile _isolatedStore = IsolatedStorageFile.GetUserStoreForApplication(); 
public static readonly FileIO _file = new FileIO(); 
public static readonly ConcurrentExclusiveSchedulerPair taskSchedulerPair = new ConcurrentExclusiveSchedulerPair(); 
public static readonly ExecutionDataflowBlockOptions exclusiveExecutionDataFlow 
    = new ExecutionDataflowBlockOptions 
{ 
    TaskScheduler = taskSchedulerPair.ExclusiveScheduler, 
    BoundedCapacity = 1 
}; 

public static readonly ExecutionDataflowBlockOptions concurrentExecutionDataFlow 
    = new ExecutionDataflowBlockOptions 
{ 
    TaskScheduler = taskSchedulerPair.ConcurrentScheduler, 
    BoundedCapacity = 1 
}; 

public static async Task<T> LoadAsync<T>(string fileName) 
{ 
    T result = default(T); 

    var transBlock = new TransformBlock<string, T> 
     (async fName => 
     { 
      return await LoadData<T>(fName); 
     }, concurrentExecutionDataFlow); 

    transBlock.Post(fileName); 

    result = await transBlock.ReceiveAsync(); 

    return result; 
} 

public static async Task SaveAsync<T>(T obj, string fileName) 
{ 
    var transBlock = new TransformBlock<Tuple<T, string>, Task> 
     (async tupleData => 
     { 
      await SaveData(tupleData.Item1, tupleData.Item2); 
     }, exclusiveExecutionDataFlow); 

    transBlock.Post(new Tuple<T, string>(obj, fileName)); 

    await transBlock.ReceiveAsync(); 
} 

MainPage.xaml.cs Uso

private static string data = "vjdsskjfhkjsdhvnvndjfhjvkhdfjkgd" 
private static string fileName = string.Empty; 
private List<string> DataLstSample = new List<string>(); 
private ObservableCollection<string> TestResults = new ObservableCollection<string>(); 
private static string data1 = "hjhkjhkhkjhjkhkhkjhkjhkhjkhjkh"; 
List<Task> allTsk = new List<Task>(); 
private Random rand = new Random(); 
private string fileNameRand 
{ 
    get 
    { 
     return rand.Next(100).ToString(); 
    } 
} 

public MainPage() 
{ 
    InitializeComponent(); 

    for (int i = 0; i < 5; i ++) 
    { 
     DataLstSample.Add((i % 2) == 0 ? data : data1); 
    } 

} 

private void Button_Click(object sender, RoutedEventArgs e) 
{ 
    AppIsolatedStore_TestInMultiThread_LstResultShouldBeEqual(); 
} 

public async void AppIsolatedStore_TestInMultiThread_LstResultShouldBeEqual() 
{ 
    TstRst.Text = "InProgress.."; 
    allTsk.Clear(); 

    foreach(var data in DataLstSample) 
    { 
     var fName = fileNameRand; 

     var t = Task.Run(async() => 
     { 
      await AppIsolatedStore.SaveAsync<string>(data, fName); 
     }); 

     TestResults.Add(string.Format("Writing file name: {0}, data: {1}", fName, data)); 
     allTsk.Add(t); 
    } 

    await Task.WhenAll(allTsk); 

    TstRst.Text = "Completed.."; 
} 

Save e Load dati asincrone

 /// <summary> 
     /// Load object from file 
     /// </summary> 
     private static async Task<T> LoadData<T>(string fileName) 
     { 

      T result = default(T); 

      try 
      { 
       if (!string.IsNullOrWhiteSpace(fileName)) 
       { 
        using (var file = new IsolatedStorageFileStream(fileName, FileMode.OpenOrCreate, _isolatedStore)) 
        { 
         var data = await _file.ReadTextAsync(file); 

         if (!string.IsNullOrWhiteSpace(data)) 
         { 
          result = JsonConvert.DeserializeObject<T>(data); 
         } 
        } 
       } 
      } 
      catch (Exception ex) 
      { 
       //todo: log the megatron exception in a file 
       Debug.WriteLine("AppIsolatedStore: LoadAsync : An error occured while loading data : {0}", ex.Message); 
      } 
      finally 
      { 

      } 

      return result; 
     } 


     /// <summary> 
     /// Save object from file 
     /// </summary> 
     private static async Task SaveData<T>(T obj, string fileName) 
     { 
      try 
      { 
       if (obj != null && !string.IsNullOrWhiteSpace(fileName)) 
       { 
        //Serialize object with JSON or XML serializer 
        string storageString = JsonConvert.SerializeObject(obj); 

        if (!string.IsNullOrWhiteSpace(storageString)) 
        { 
         //Write content to file 
         await _file.WriteTextAsync(new IsolatedStorageFileStream(fileName, FileMode.Create, _isolatedStore), storageString); 
        } 
       } 
      } 
      catch (Exception ex) 
      { 
       //todo: log the megatron exception in a file 
       Debug.WriteLine("AppIsolatedStore: SaveAsync : An error occured while saving the data : {0}", ex.Message); 
      } 
      finally 
      { 
      } 
     } 

Modifica:

La ragione per cui sta avendo un'eccezione di memoria è dovuta a una ragione per cui la stringa di dati che ho preso è troppo grande. La stringa è link: http://1drv.ms/1QWSAsc

Ma il secondo problema è che se aggiungo piccoli dati anche questo blocca il thread dell'interfaccia utente. Il codice sta svolgendo qualsiasi compito sul battistrada dell'interfaccia utente?

risposta

1

No, si utilizza la coppia simultanea che utilizza il pool di thread predefinito per le sue attività e si istanziano le attività con il metodo Run, quindi il problema non è qui. Ma il codice che avete qui ha due minacce principali:

var transBlock = new TransformBlock<string, T> 
    (async fName => 
    { 
     // process file here 
    }, concurrentExecutionDataFlow); 

Davvero non dovrebbe creare transBlock ogni volta. L'idea principale di TPL Dataflow è che stai creando i blocchi una volta e usali dopo. Quindi dovresti rifattorizzare la tua app per ridurre il numero di blocchi da istanziare, altrimenti questo non è il caso che dovrebbe essere usato TPL Dataflow.

Un'altra minaccia nel codice è che si blocca esplicitamente la discussione!

// Right here 
await Task.WhenAll(allTsk); 
TstRst.Text = "Completed.."; 

Calling un await per un'attività da un metodo async void da un synchronious blocchi gestore eventi il ​​filo, come per default it captures the synchronization context. Prima di tutto, async void should be avoided. In secondo luogo, se si è asincroni, si should be async all the way, quindi il gestore eventi dovrebbe essere anche asincrono. In terzo luogo, è possibile utilizzare uno continuation for your task per aggiornare l'interfaccia utente o use current synchronization context.

Quindi, il codice dovrebbe essere qualcosa di simile:

// store the sync context in the field of your form 
SynchronizationContext syncContext = SynchronizationContext.Current; 

// avoid the async void :) 
public async Task AppIsolatedStore_TestInMultiThread_LstResultShouldBeEqual() 

// make event handler async - this is the only exception for the async void use rule from above 
private async void Button_Click(object sender, RoutedEventArgs e) 

// asynchronically wait the result without capturing the context 
await Task.WhenAll(allTsk).ContinueWith(
    t => { 
    // you can move out this logic to main method 
    syncContext.Post(new SendOrPostCallback(o => 
     { 
      TstRst.Text = "Completed.."; 
     })); 
    } 
); 
+0

Sto esplorando i modi per rendere le operazioni di IO funzione thread-safe senza utilizzare il meccanismo di blocco. Per evitare effetti collaterali del bloccaggio. Come dici tu, dovrei ridurre il numero di creazione di blocchi o usare un approccio diff. Puoi suggerire un modo per migliorarlo o un nuovo modo che posso esplorare di più. –

+0

@BalrajSingh TPL Dataflow utilizza ancora blocchi internamente. Le istruzioni manuali 'lock' sono molto più leggibili e molto più efficienti – VMAtm

+1

Nell'esempio finale,' ConfigureAwait' è sbagliato - questo non si compilerebbe. Inoltre, 'ContinueWith' non dovrebbe essere usato. Se l'op sta usando TPL Dataflow, una soluzione più idiomatica sarebbe un 'ActionBlock' finale con un'interfaccia utente' TaskScheduler'. –

0

Hai provato a giocare con il parametro BoundedCapacity su ExecutionDataflowBlockOptions? Il Introduction to TPL menzioni questo sulla capacità blocco:

[...] delimitazione è utile in una rete flusso di dati per evitare memoria illimitata crescita. Questo può essere molto importante per ragioni di affidabilità se c'è una possibilità che i produttori potrebbero finire per la generazione di dati molto più veloce rispetto alle consumatori potrebbero elaborarlo ...

Id suggerire cercando di utilizzare questa opzione questo limite la messa in coda di articoli elaborati e vedere se aiuta con i tuoi problemi di memoria

+0

ho impostato BoundingCapacity a 1 ancora il problema persiste. –