6

申し訳ありませんが、タイトルが少しくだらないので、正しく表現できませんでした。

編集:これはコンソールc#アプリであることに注意してください

私はそのように機能するシステムのプロトタイプを作成しました(これは大まかな疑似コードです):

var collection = grabfromdb();

foreach (item in collection) {
    SendAnEmail();
}

メールを送る:

SmtpClient mailClient = new SmtpClient;
mailClient.SendCompleted += new SendCompletedEventHandler(SendComplete);
mailClient.SendAsync('the mail message');

SendComplete:

if (anyErrors) {
    errorHandling()
}
else {
    HitDBAndMarkAsSendOK();    
}

明らかに、この設定は理想的ではありません。最初のコレクションにたとえば10,000レコードがある場合、行をステップスルーできる限り迅速に、smtpclientの10,000インスタンスをかなり短い順序で新規作成します。

私の理想的なエンドゲームは、10通の同時メールを一度に送信することです。

ハッキーな解決策が思い浮かびます。SendAnEmail()が呼び出されると増加し、SendCompleteが送信されると減少するカウンターを追加します。最初のループでSendAnEmail()が呼び出される前に、カウンターをチェックします。カウンターが高すぎる場合は、少しの間スリープしてから、もう一度チェックします。

それがそんなに素晴らしいアイデアかどうかはわかりませんが、SOハイブマインドがこれを適切に行う方法があると考えています。

私はスレッディングについてほとんど知識がなく、ここでそれが適切な使用法であるかどうかはわかりません。たとえば、バックグラウンドスレッドでメールを送信する場合は、最初に子スレッドの数をチェックして、使用されているスレッドが多すぎないことを確認します。または、何らかのタイプの「スレッドスロットリング」が組み込まれている場合。


アップデート

スティーブンA.ロウのアドバイスに従って、私は今持っています:

  • 私の電子メールと一意のキーを保持する辞書(これは電子メールのキューです
  • 辞書に入力するFillQueメソッド
  • バックグラウンドスレッドであるProcessQueメソッド。キューをチェックし、キュー内のすべての電子メールをSendAsycsします。
  • キューから電子メールを削除するSendCompletedデリゲート。そして、FillQueを再度呼び出します。

この設定にはいくつか問題があります。バックグラウンドスレッドでボートを逃したと思いますが、辞書の各アイテムに対してこれらの1つをスポーンする必要がありますか?電子メールのキューがスレッドの終わりを空にした場合、より良い単語がないためにスレッドを「ぶらぶら」させるにはどうすればよいですか。


最終更新

'while(true){}'をバックグラウンドスレッドに入れました。キューが空の場合、数秒待ってから再試行します。キューが繰り返し空になる場合は、しばらくの間「中断」してプログラムを終了します...正常に動作します。私は「while(true)」ビジネスについて少し心配しています。

4

5 に答える 5

2

簡潔な答え

独自のスレッドによって処理される有限バッファとしてキューを使用します。

長い答え

fill-queue メソッドを呼び出して、(たとえば) 10 個に制限された電子メールのキューを作成します。最初の 10 個の未送信の電子メールで埋めます。スレッドを起動してキューを処理します - キュー内の電子メールごとに非同期で送信します。キューが空になったら、しばらくスリープしてから再度確認します。完了デリゲートに、送信済みまたはエラーが発生した電子メールをキューから削除してデータベースを更新させてから、fill-queue メソッドを呼び出して、さらに未送信の電子メールをキューに読み込みます (制限まで戻します)。

キュー操作に関するロックのみが必要であり、キューを処理する 1 つのスレッドのみを (直接) 管理する必要があります。一度に N+1 を超えるスレッドがアクティブになることはありません。ここで、N はキューの制限です。

于 2009-03-18T04:56:46.733 に答える
1

I believe your hacky solution actually would work. Just make sure you have a lock statement around the bits where you increment and decrement the counter:

class EmailSender
{
  object SimultaneousEmailsLock;
  int SimultaneousEmails;
  public string[] Recipients;

  void SendAll()
  {
    foreach(string Recipient in Recipients)
    {
      while (SimultaneousEmails>10) Thread.Sleep(10);
      SendAnEmail(Recipient);
    }
  }

  void SendAnEmail(string Recipient)
  {
    lock(SimultaneousEmailsLock)
    {
      SimultaneousEmails++;
    }

    ... send it ...
  }

  void FinishedEmailCallback()
  {
    lock(SimultaneousEmailsLock)
    {
      SimultaneousEmails--;
    }

    ... etc ...
  }
}
于 2009-03-18T04:40:22.183 に答える
1

すべてのメッセージをキューに追加してから、キューが空になるまで電子メールを送信する 10 個のスレッドを生成します。疑似 C# (おそらくコンパイルしない):

class EmailSender
{
    Queue<Message> messages;
    List<Thread> threads;

    public Send(IEnumerable<Message> messages, int threads)
    {
        this.messages = new Queue<Message>(messages);
        this.threads = new List<Thread>();
        while(threads-- > 0)
            threads.Add(new Thread(SendMessages));

        threads.ForEach(t => t.Start());

        while(threads.Any(t => t.IsAlive))
            Thread.Sleep(50);
    }

    private SendMessages()
    {
        while(true)
        {
            Message m;
            lock(messages)
            {
                try
                {
                    m = messages.Dequeue();
                }
                catch(InvalidOperationException)
                {
                    // No more messages
                    return;
                }
            }

            // Send message in some way. Not in an async way, 
            // since we are already kind of async.

            Thread.Sleep(); // Perhaps take a quick rest
        }
    }
}

メッセージが同じで、多くの受信者がいる場合は、メッセージを受信者と交換し、単一のメッセージ パラメーターを Send メソッドに追加します。

于 2009-03-18T07:22:37.957 に答える
0

You could use a .NET Timer to setup the schedule for sending messages. Whenever the timer fires, grab the next 10 messages and send them all, and repeat. Or if you want a general (10 messages per second) rate you could have the timer fire every 100ms, and send a single message every time.

If you need more advanced scheduling, you could look at a scheduling framework like Quartz.NET

于 2009-03-18T04:42:55.017 に答える
0

これはなんとかなるものではないThread.Sleep()か。

ここでは、バックグラウンド スレッドが適切な目的を果たすことができると考えるのは正しいことです。基本的にやりたいことは、このプロセスのバックグラウンド スレッドを作成し、それを独自の方法で実行させ、すべてを遅延させ、プロセスが完了したらスレッドを終了するか、無期限にそのままにしておくことです (Windows サービスまたは似たようなものは良い考えです)。

マルチスレッドの簡単な紹介は、ここで読むことができます (Thread.Sleep が含まれています!) 。

Windows サービスの優れた紹介は、ここで読むことができます

于 2009-03-18T04:56:19.853 に答える