3

与えられた CookieContainer のインスタンスはスレッドセーフではありません

この型の public static (Visual Basic では共有) メンバーはすべて、スレッド セーフです。インスタンス メンバーは、スレッド セーフであるとは限りません。

したがって、同期せずに複数の同時 HTTP リクエストで同じコンテナーを使用することはできません。残念ながら、MSDN のドキュメントからは、適切に同期する方法が明確ではありません。

解決策は、リクエストごとにマスター コンテナーのコピーを使用することです。リクエストが完了すると、コピーからの Cookie をマスター コンテナーにマージすることができます。コピーの作成とマージは、同期して行うことができます。

問題は、CookieContainer クラスのインスタンスのコピーを作成するにはどうすればよいかということです。

4

5 に答える 5

7

CookieContainer クラスはシリアライズ可能です。とにかくシリアル化する必要があると言ったので、 BinaryFormatter を使用して MemorySteam にシリアル化し、それを Deserialize してコピーを作成してみませんか?

これは単純すぎることはわかっているので、役に立たない場合は無視してください。

private CookieContainer CopyContainer(CookieContainer container)
{
    using(MemoryStream stream = new MemoryStream())
    {
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Serialize(stream, container);
        stream.Seek(0, SeekOrigin.Begin);
        return (CookieContainer)formatter.Deserialize(stream);
    }
}
于 2013-08-21T00:24:09.810 に答える
5

CookieContainterクラスを見ると、Cookie コレクションに変更があった場合に同時発生シナリオが発生することが想定されていることがわかりますよね?

CookieContainer の作成者が、コードのコレクションを変更するこれらの部分の使用lock {}SyncRootすべての処理を行ったことに気付くでしょう。そのようなアプローチが同時発生シナリオに対応していないとは思いません。

また、追加されたCookieは文字通りclonedであるため、コンテナー内の Cookie と行われたすべての操作が、Cookie コンテナーの外部のオブジェクト参照を台無しにすることはありません。何かが欠けているという最悪のケースでは、クローンは、他の投稿で説明されているリフレクションアプローチを使用する場合に、正確に何をコピーする必要があり、どのようにそれを行うことができるかについてのヒントも提供します (私は個人的には考慮しません)それは要件に適合し、管理され、合法で安全なコードであるため、ハックです:))。

実際、MSDN ドキュメント全体で言及されているのは、「すべてのインスタンス メンバーがスレッド セーフであることが保証されていない」ということです。-それは一種のリマインダーです。あなたが正しいので、本当に注意する必要があります。次に、そのようなステートメントを使用して、基本的に 2 つのことを推測できます。1) 非静的メンバーはまったく安全ではありません。2) 一部のメンバーはスレッド セーフにできますが、適切に文書化されていません。

于 2013-08-22T01:21:21.707 に答える
3

Reflection を使用して、すべての に関連するすべてUriの Cookie を取得し、新しいものを作成CookieContainerして追加することができます。たとえば、次のようになります。

public static CookieContainer DeepClone(CookieContainer src)
{
    CookieContainer cookieContainer = new CookieContainer();

    Hashtable table = (Hashtable)src.GetType().InvokeMember("m_domainTable", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance, null, src, new object[] { });

    foreach (var tableKey in table.Keys)
    {
        String str_tableKey = (string)tableKey;

        if (str_tableKey[0] == '.')
            str_tableKey = str_tableKey.Substring(1);

        SortedList list = (SortedList)table[tableKey].GetType().InvokeMember("m_list", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance, null, table[tableKey], new object[] { });

        foreach (var listKey in list.Keys)
        {
            String url = "https://" + str_tableKey + (string)listKey;

            CookieCollection collection = src.GetCookies(new Uri(url));

            foreach (Cookie c in collection)
                cookieContainer.Add(new Cookie(c.Name, c.Value, c.Path, c.Domain)
                {
                    Comment = c.Comment,
                    CommentUri = c.CommentUri,
                    Discard = c.Discard,
                    Expired = c.Expired,
                    Expires = c.Expires,
                    HttpOnly = c.HttpOnly,
                    Port = c.Port,
                    Secure = c.Secure,
                    Version = c.Version
                });
        }
    }
    return cookieContainer;
}
于 2013-08-21T00:18:31.197 に答える