もなーのあれこれ

血に飢えたハムスターに飢えたあかちゃん

【アズレン完全攻略wiki】初心者必見!!イベント「黒鉄の楽章、誓の海」無課金周回編成はこれで決まり!!

はじめに

マルチスレッドプログラミングでファイルを扱う話。
ファイルに複数スレッドから同時にアクセスするとデータの整合性が取れなかったりするので基本的にはロックを掛けることが多いかと思います。

そんなこんなでこないだ悩む羽目になったので今日の記事

複数スレッドから書き込みと読み込みを同時に行うサンプルが↓

form1.cs

public partial class Form1 : Form
    {
        #region "コンストラクタ"
        public Form1()
        {
            InitializeComponent();
            threadflag = false;
            if (!Directory.Exists("test"))
            {
                Directory.CreateDirectory("test");
            }
        }
        #endregion

        #region "イベントハンドラ"
        private void startbutton_Click(object sender, EventArgs e)
        {
            threadflag = true;
            var wt = new Thread(new ThreadStart(WriteThreadMethod));
            var rt = new Thread(new ThreadStart(ReadThreadMethod));
            wt.Start();
            rt.Start();
        }
        private void stopbutton_Click(object sender, EventArgs e)
        {
            threadflag = false;
        }
        #endregion

        #region "サブスレッド用メンバ"
        /// <summary>
        /// スレッド終了フラグ
        /// </summary>
        private Boolean threadflag;
        /// <summary>
        /// ファイル書き込みスレッド用
        /// </summary>
        private void WriteThreadMethod()
        {
            while (threadflag)
            {
                var filename = $"{DateTime.Now.ToString("yyyyMMddHHmmss")}.txt";
                var textvalue = DateTime.Now.ToString();
                var frw = new FileRW($@"test\{filename}");
                frw.FileWrite(textvalue);
                Thread.Sleep(1000);
            }
        }
        /// <summary>
        /// ラベル更新用デリゲート
        /// </summary>
        /// <param name="value">テキスト</param>
        private delegate void labeldelegate(String value);
        /// <summary>
        /// ファイル読み込みスレッド用
        /// </summary>
        private void ReadThreadMethod()
        {
            var di = new DirectoryInfo("test");
            while (threadflag)
            {
                foreach (FileInfo fi in di.GetFiles())
                {
                    var frw = new FileRW(fi.FullName);
                    if (InvokeRequired)
                    {
                        this.Invoke(new labeldelegate((String text) => { textlabel.Text = text; }), new Object[] { frw.FileRead() });
                    }
                    else
                    {
                        textlabel.Text = frw.FileRead();
                    }
                    Thread.Sleep(500);
                }
            }
        }
        #endregion
    }


FileRW.cs

class FileRW
    {
        public String targetfile { get; set; }
        public FileRW(String targetfile)
        {
            this.targetfile = targetfile;
        }
        /// <summary>
        /// テキストファイル書き込み
        /// </summary>
        /// <param name="value">テキスト文字列</param>
        public void FileWrite(String value)
        {
            try
            {
                using (var fs = new FileStream(targetfile, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
                using (var sw = new StreamWriter(fs))
                {
                    Thread.Sleep(5000); //アクセス競合のためにわざと止まる。
                    sw.WriteLine(value);
                }
            }
            catch (Exception e)
            {

            }
        }
        /// <summary>
        /// テキストファイル読み込み
        /// </summary>
        /// <returns></returns>
        public String FileRead()
        {
            String result;
            try
            {
                using (var fs = new FileStream(targetfile, FileMode.Open, FileAccess.Read, FileShare.None))
                using (var sr = new StreamReader(fs))
                {
                    result = sr.ReadLine();
                }

            }
            catch (Exception e)
            {
                result = "ERROR!";
            }
            return result;
        }
    }

時間をテキストファイルに次々出力していくスレッドと、出力されたテキストファイルを読み込んでいってテキストファイルの内容でラベルを更新していくって感じです。

ただ上記ソースだと書き込み中のファイルに対して読み込みを行うと別のプロセスが~とか言われて読み込めないタイミングがあるます。

f:id:Monaaaaa:20190525140932p:plain

こんな感じ。(どんな感じ?)

ReadFile()の前にファイルにアクセスできるかどうか調べてぇな...となったんだけど、FileInfoクラスにそういったプロパティとかメソッドないっぽい?ので

FileRWクラスに下記メソッドを追加して

        /// <summary>
        /// ファイルアクセス可否判定
        /// </summary>
        /// <returns>true→アクセス不可、false→アクセス可能</returns>
        public Boolean FileIsLocked()
        {
            try
            {
                using (var fs = new FileStream(targetfile, FileMode.Open, FileAccess.Read, FileShare.None))
                {
                }
            }
            catch(Exception e)
            {
                return true;
            }
            return false;
        }

Form1クラスのReadThreadMethod()内でファイルを読み込む直前に↓みたいな感じで待機する風に書き換えました。

       private void ReadThreadMethod()
        {
            var di = new DirectoryInfo("test");
            while (threadflag)
            {
                foreach (FileInfo fi in di.GetFiles())
                {
                    var frw = new FileRW(fi.FullName);

                    //アクセス不可だったら少し待つ
                    while (frw.FileIsLocked() == true) Thread.Sleep(500);

                    if (InvokeRequired)
                    {
                        this.Invoke(new labeldelegate((String text) => { textlabel.Text = text; }), new Object[] { frw.FileRead() });
                    }
                    else
                    {
                        textlabel.Text = frw.FileRead();
                    }
                    Thread.Sleep(500);
                }
            }
        }

これで後続のアクセスが書き込み中のプロセスを待つような処理が実現できました(完)
まぁ、この記事の方法だと別の理由でファイルにアクセスできなかった場合すべてが崩壊するんだけどね(照)

なんかいい方法ないですかね~♨

というわけで今日のおまけはここまでです。

本編

アズレンやってるかい?

僕はシャドウバースばかりしてました///
僕がアズレンから逃げたというよりはアズレンが俺から逃げていったといった感じでしょうか。

ですがシャドウバースでボコボコにされすぎて頭がおかしくなってアズレン再開しました。(パチパチパチ)

f:id:Monaaaaa:20190525143938j:plain

UIが色々変わりすぎてワロタです。

こういう金にならないところも頑張ってくれるの普通にいいですね。
キズナアイはカスだけど。

で、前回のあちゃちゃんと今回のビスマルク追加で鉄血陣営の主力艦隊増えて良かったなぁと思います。
ホントこのままだとどうやってガスコーニュとかフリードニヒ解禁するんだって感じだったので。

今回のイベントで解禁目指したいです。

おわりに

あかご
f:id:Monaaaaa:20190525145013j:plain