5

私は.NET4.5の変更点、主に非同期機能に頭を悩ませようとしています。頭を悩ませるために、膨大な写真コレクションをアーカイブするための小さなアプリを作成しようと思いました。私はそうすることで最もよく学びます。そうすることで、アプリケーションは2つの目的を果たします。

非同期の使用に関するMSDNの記事をたくさん読んだことがありますが、(機能していないため)非同期について十分に理解していないと思います。私の意図は、撮影日(または撮影したメタデータがない場合は作成)に基づいて、ソースフォルダーの各写真を宛先フォルダーにコピーすることでした。同時に、名前を標準の命名規則に変更し、画像ボックスにアーカイブされた画像を表示します。非同期が発生する作業中もアプリケーションが応答し続けるようにしたかったのです。アプリの目的は重要ではなくなったので、全体のポイントは非同期に頭を悩ませることでした。

実際に発生するのは、アプリが応答しなくなり、意図したとおりにすべての画像をアーカイブしますが、画像ボックスには最終的な画像のみが表示されます。Asyncはファイル転送を開始してから次の画像に移動し、転送を開始してから次に進むなどのように、それぞれが閉じるのを待つのではなく、何百もの開いているファイルストリームになってしまいます。

私が間違っているところのポインタをいただければ幸いです。タスクの使用についての私の理解は不安定ですが、タスクを返すことはどのような目的に役立ちますか?

imgMainは、XAMLファイルのイメージボックスです。async / awaitはarchiveメソッドにありますが、関連する可能性があるため、すべてのコードが表示されます。

using System;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Media.Imaging;
using System.Windows.Forms;
using System.IO;

namespace PhotoArchive
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{

    private string Source 
    {
        get { return txtSource.Text; }
        set { txtSource.Text = value; }
    }

    private string Destination
    {
        get { return txtDestination.Text; }
        set { txtDestination.Text = value; }
    }


    public MainWindow()
    {
        InitializeComponent();

    }

    private void btnBrowseDataSource_Click(object sender, RoutedEventArgs e)
    {
        var dialogue = new FolderBrowserDialog();
        dialogue.ShowDialog();
        Source = dialogue.SelectedPath;

    }

    private void btnBrowseDestination_Click(object sender, RoutedEventArgs e)
    {
        var dialogue = new FolderBrowserDialog();
        dialogue.ShowDialog();
        Destination= dialogue.SelectedPath;
    }

    private void btnSort_Click(object sender, RoutedEventArgs e)
    {
        var files = Directory.GetFiles(Source, "*.*", SearchOption.AllDirectories);
        var result = from i in files
                     where i.ToLower().Contains(".jpg") || i.ToLower().Contains(".jpeg") || i.ToLower().Contains(".png")
                     select i;


        foreach (string f in result)
        {
            DateTime dest = GetDateTakenFromImage(f);
            Archive(f, Destination, dest);
        }

    }

    private async void Archive(string file, string destination, DateTime taken)
    {

        //Find Destination Path
        var sb = new StringBuilder();
        sb.Append(destination);
        sb.Append("\\");
        sb.Append(taken.ToString("yyyy"));
        sb.Append("\\");
        sb.Append(taken.ToString("MM"));
        sb.Append("\\");

        if (! Directory.Exists(sb.ToString()))
        {
            Directory.CreateDirectory(sb.ToString());
        }

        sb.Append(taken.ToString("dd_MM_yyyy_H_mm_ss_"));
        sb.Append((Directory.GetFiles(destination, "*.*", SearchOption.AllDirectories).Count()));
        string[] extension = file.Split('.');
        sb.Append("." + extension[extension.Length-1]);


        using (FileStream fs = File.Open(file, FileMode.Open))
        using (FileStream ds = File.Create(sb.ToString())) 
        {
            await fs.CopyToAsync(ds);
            fs.Close();
            File.Delete(file);
        }

        ImgMain.Source = new BitmapImage(new Uri(sb.ToString()));
    }

    //get date info
    private static Regex r = new Regex(":");

    public static DateTime GetDateTakenFromImage(string path)
    {
        using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
        {
            using (System.Drawing.Image img = System.Drawing.Image.FromStream(fs, false, false))
            {
                PropertyItem prop;

                try
                {

                    prop = img.GetPropertyItem(36867);

                }
                catch (Exception)
                {
                    prop = img.GetPropertyItem(306);
                }

                string dateTaken = r.Replace(Encoding.UTF8.GetString(prop.Value), "-", 2);
                return DateTime.Parse(dateTaken);
            }
        }


    }
}

}

4

2 に答える 2

6

タスクの使用に関する私の理解は不安定です。タスクを返すことはどのような目的に役立ちますか?

Task非同期操作を表しています。がTask完了すると、操作が完了したことを意味します。awaitつまりTask、完了するまで非同期的に待機します (UI スレッドをブロックしません) 。

しかし、メソッドを にするasync voidと、操作が完了するのを待つ方法がありません。メソッドが戻ると、非同期操作が開始されたことがわかりますが、それだけです。

必要なのは、Archive()を返すように変更しTaskて、イベント ハンドラーで完了するのを待つことができるようにすることです。はTask自動的に返されます。 を追加する必要はありません (追加することもできます) return

したがって、署名を次のように変更しますArchive()

private async Task Archive(string file, string destination, DateTime taken)

そして、awaitそれをイベント ハンドラーで (これも に変更する必要がありますasync):

private async void btnSort_Click(object sender, RoutedEventArgs e)
{
    // snip

    foreach (string f in result)
    {
        DateTime dest = GetDateTakenFromImage(f);
        await Archive(f, Destination, dest);
    }
}

一般に、async voidメソッドはイベント ハンドラーに対してのみ使用する必要があります。他のすべてのasyncメソッドはasync Task(またはasync Task<SomeType>それらが何らかの値を返す場合)、そうする必要がawaitあります。

于 2013-02-05T11:35:50.813 に答える
0

Archiveメソッドの 1 つのインスタンスのみArchiveを任意の時点で実行する必要があるため、メソッドを待機する必要があります。実装では、多くのArchiveインスタンスを開始していて、実際には UI スレッドを解放していないことに注意してください。

コードの変更:

  • asyncに追加btnSort_Click
  • 戻り型Taskを追加Archive
  • Archiveで待ちますbtnSort_Click

ヒント: (あなたのケースbtnSort_Clickでは) 呼び出された最初のメソッドが非同期でない場合、「外部から」非同期とは見なされません。つまり、ウィンドウと UI スレッドです。

于 2013-02-05T11:09:45.283 に答える