2015-12-23 26 views
6

Ich habe versucht, Schreiboperation mit File IO-Operationen zu implementieren und diese Operationen in TransformBlock eingekapselt, um diese Operation thread sicher zu machen, anstatt Sperrmechanismus zu verwenden.Speicherproblem in TPL Dataflow-Implementierung von IO-Lese-Schreibvorgang

Aber das Problem ist, dass, wenn ich versuche, sogar 5 Dateien parallel zu schreiben, gibt es einen Speicher außerhalb der Ausnahme und bei der Verwendung dieser Implementierung blockiert es UI-Thread. Die Implementierung erfolgt in Windows Phone-Projekt. Bitte schlagen Sie vor, was in dieser Implementierung falsch ist.

Datei IO Betrieb

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 Nutzungs

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.."; 
} 

Speichern und Laden von Daten Async

 /// <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 
      { 
      } 
     } 

Bearbeiten:

Der Grund, dass es Speicherausnahme hat, ist aus einem Grund, dass die Datenfolge, die ich nahm, zu groß ist. Die Zeichenfolge ist Link: http://1drv.ms/1QWSAsc

Aber das zweite Problem ist, dass, wenn ich kleine Daten auch dann hinzufügen, es UI-Thread blockiert. Führt Code auf dem UI-Profil eine Aufgabe aus?

Antwort

1

Nein, Sie verwenden das gleichzeitige Paar, das den Standardthread-Pool für seine Aufgaben verwendet, und Sie Instanziieren Aufgaben mit Run Methode, damit das Problem nicht hier ist. Aber der Code, den Sie hier haben zwei Hauptbedrohungen:

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

Sie sollten wirklich nicht schaffen transBlock jedes Mal. Die Grundidee von TPL Dataflow ist, dass Sie die Blöcke einmal erstellen und danach verwenden. Sie sollten Ihre App daher auf die Anzahl der Blöcke, die Sie instanziieren, umstellen. Andernfalls ist TPL Dataflow nicht erforderlich.

Eine weitere Bedrohung in Ihrem Code ist, dass Sie den Thread explizit blockieren!

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

ein await zur Aufgabe von einer async void Methode aus ein synchronious Ereignisbehandlungsblöcken den Thread aufrufen, wie durch Standard it captures the synchronization context. Zunächst einmal async void should be avoided. Zweitens, wenn Sie async sind, Sie should be async all the way, so Event-Handler sollte auch async sein. Drittens können Sie eine continuation for your task für die Aktualisierung Ihrer UI oder use current synchronization context verwenden.

Also, sollten Sie Ihren Code so etwas wie dieses:

// 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

Ich erkunde Wege, um IO-Operationen threadsicher zu machen, ohne den Sperrmechanismus zu verwenden. Um Nebenwirkungen der Verriegelung zu vermeiden. Wie Sie sagen, sollte ich die Anzahl der Blockerstellung reduzieren oder einen Diff-Ansatz verwenden. Kannst du einen Weg vorschlagen, dies besser zu machen oder einen neuen Weg, den ich mehr erkunden kann? –

+0

@BalrajSingh TPL Dataflow verwendet intern noch Blöcke. Manuelle 'lock'-Anweisungen sind viel besser lesbar und viel effizienter. – VMAtm

+1

Im letzten Beispiel ist' ConfigureAwait' falsch - das würde nicht kompilieren. Außerdem sollte 'ContinueWith' nicht verwendet werden. Wenn das op TPL Dataflow verwendet, wäre eine idiomatische Lösung ein finaler "ActionBlock" mit einem UI "TaskScheduler". –

0

Haben Sie versucht, mit dem BoundedCapacity Parameter auf den ExecutionDataflowBlockOptions zu spielen? Die Introduction to TPL erwähnt dies über Blockkapazität:

[...] Begrenzungs in einem Datenfluß Netzwerk nützlich sind unbegrenzt Speicher Wachstum zu vermeiden. Dies kann aus Gründen der Zuverlässigkeit sehr wichtig sein, wenn es ist eine Möglichkeit, dass die Hersteller Erzeugen von Daten viel schneller könnten am Ende als die Verbraucher sie verarbeiten könnten ...

Id schlagen versuchen, diese Option zu verwenden, diese Grenze die Warteschlange stellen Verarbeitete Elemente und sehen, ob es mit Ihren Speicherproblemen hilft

+0

Ich habe gesetzt BoundingCapacity 1 das Problem weiterhin bestehen bleiben. –