2020年に選ばれたインタビューの質問と回答(1)

1. ACMコンペティションでは、チームは3人のプレーヤーで構成されます。現在N + M人の学生がいます。そのうち、N人の学生はアルゴリズムが得意です。残りのM人の学生はプログラミングが得意です。これらの学生はACMコンペティションに参加する必要があります。彼らのコーチ各チームには、アルゴリズムが得意な学生とプログラミングが得意な学生が少なくとも1人必要です。これらの学生は、いくつのチームを編成できますか?

入力:2つの整数MとNを入力します。ここで、1 <N、M <10000000
出力:形成できるチームの最大数

#include <iostream>
using namespace std;
 
int main()
{
    
    
    int cnt = 0,n,m;
    cout << "输入N个擅长算法的,M个擅长编程的:" << endl;
    cin >> n >> m;
    while(n!=0&&m!=0&&m+n!=2){
    
    
        if(n>=m){
    
    
            n = n-2;
            m = m-1;
            cnt++;
        }
        else if(n<m){
    
    
            m = m-2;
            n = n-1;
            cnt++;
        }
    }
    cout << "最大组对数量" << cnt << endl;
    return 0;
}

2.同一性とは何ですか

イデモポテンスの概念:一般的に、イデモポテンスとは、何度操作を繰り返しても同じ結果が得られることを意味します。

3.RESTリクエストの独立した操作とは何ですか

GET、PUT、およびDELETEはすべて同一の操作ですが、POSTは
分析ではありません
まず、GET要求は十分に理解されています。リソースを複数回クエリする場合、この実装の結果は同じです。
PUT要求の同一性は、このように理解できます。AをBに変更すると、最初の要求値がBになり、この操作を何度も実行しても、最終結果はBのままです。これは、1回の実行の結果と同じであるため、PUTこれは、同一の操作です。
同様に、DELETE操作を理解することができます。リソースが最初に削除された後、削除要求が複数回行われ、最終結果は同じになり、リソースが削除されます。
1つのリクエストが新しいリソースを追加し、2番目のリクエストが2つの新しいリソースを追加するため、POSTは独立した操作ではありません。複数のリクエストは異なる結果を生成するため、POSTは独立した操作ではありません。

4.同一性に基づいてPOSTとPUTの使用を区別します

idempotent(idempotent)によって区別することができます。
簡単な例を挙げると、Web APIを提供するブログシステムがある場合、モードは次のようになりますhttp:// superblogging / blogs / {blog-name}、非常に単純です。{blog-name}をブログ名に置き換えてください。このURLはHTTPPUTまたはPOSTリクエストを送信します。HTTPの本文部分はブログ投稿です。これは非常に単純なRESTAPIの例です。
PUTメソッドとPOSTメソッドのどちらを使用する必要がありますか?
このRESTサービスの動作が同じであるかどうかによって異なりますが、2つのhttp:// superblogging / blogs / post / Sampleリクエストを送信すると、サーバー側でどのような動作になりますか?2つのブログ投稿が生成された場合、複数回の使用には副作用があるため、このサービスは同一ではないことを意味します。最初の要求が後者の要求によって上書きされた場合、サービスは同一です。前者の場合はPOST方式を使用し、後者の場合はPUT方式を使用する必要があります。

5.CASとそのソリューションの欠点。

CASの欠点は、ABAの問題、スピンロックの消費の問題、多変数共有の一貫性の問題など
です。1。ABA:
問題の説明:スレッドt1は、値をAからBに変更し、次にBからAに変更します。同時に、スレッドt2は値をAからCに変更しようとしています。しかし、CASがチェックすると、変更がないことがわかりますが、実際には変更されています。データが欠落する可能性があります。
解決策:CASは依然として楽観的ロックに似ており、AtomicStampedReference
2などのデータの楽観的ロックと同じ方法でバージョン番号またはタイムスタンプを追加します。スピンはリソースを消費します:
問題の説明:複数のスレッドが同じリソースをめぐって競合する場合、スピンする場合失敗した場合、CPUは常に占有されます。
解決策:for無限ループを破棄します。特定の時間または回数を超えると、リターンが終了します。JDK8で追加されたLongAddrは、ConcurrentHashMapに似ています。複数のスレッドが競合する場合、粒度が低下し、1つの変数が複数の変数に分割されて、複数のスレッドが複数のリソースにアクセスする効果が得られます。最後に、合計が呼び出されてそれらが結合されます。
ベースとセルの両方が揮発性に変更されていますが、合計操作がロックされていないように感じられ、合計の結果はそれほど正確ではない可能性があります。
2.多変数共有の一貫性の問題:
解決策:複数の変数を操作する場合、CAS操作は1つの変数に対して行われます。

  1. ロックで解決できます。
  2. 解決するオブジェクトクラスにパッケージ化されています。
    ここに写真の説明を挿入
    (より無料のC / C ++、Linux、Nginx、ZeroMQ、MySQL、Redis、fastdfs、MongoDB、ZK、ストリーミングメディア、CDN、P2P、K8S、Docker、TCP / IP、coroutine、DPDKなど。複数のナレッジポイントが乾物学習教材とグループ960994558)

6. B +ツリーの機能

(1)各ノードのキーワードの数は、子の数と同じです。最も低い内部ノード以外のすべてのキーワードは、対応するサブツリーの最大のキーワードであり、最も低い内部ノードにはすべてのキーワードが含まれます。 。
(2)ルートノードを除いて、各内部ノードには最大m個の子があります。[3]
(3)すべてのリーフノードはツリー構造の同じレベルにあり、情報が含まれていません(外部ノードまたは検索に失敗したノードと見なすことができます)。したがって、ツリー構造は常にツリーの高さでバランスが取れています。

7.トランザクションの分離について詳しく説明します。

単離
トランザクションの分離を確実にするためにトランザクションが、私たちは自然にシングルスレッドであることをトランザクションを設計することができます。この方法では、効率が非常に低くなります。効率を失うことなく絶縁を確保するために、我々は3つの状況に隔離の損失を分割します。
ダーティ読み取り:コミットされていない別のトランザクションのデータを
読み取りますファントム読み取り:トランザクション中にテーブルが1回読み取られました。この時点で、別のトランザクションがコミットされているため、トランザクションはテーブルの読み取りに一貫性を欠いています。(テーブルへの影響)
繰り返し不可の読み取り:トランザクション中にaのデータが1回読み取られ、この時点で別のトランザクションがコミットされたため、トランザクションは再び一貫性のないデータを読み取ります。
これらの3つの状況に対して、4つの分離レベルが導入されています
。4つの分離レベル:
コミットされていない読み取り-分離の問題を防止せず、ダーティ読み取り/非再現性/ファントム読み取り(ファントム読み取り)の問題
読み取りコミット-ダーティ読み取りの問題を防止できます。ただし、非反復性/ファントム読み取り(ファントム読み取り)の問題を防ぐことはできません
繰り返し可能読み取り-ダーティ読み取り/非反復可能読み取りの問題を防ぐことはできますが、仮想読み取り(ファントム読み取り)の問題を防ぐことはできません
シリアル化可能-データベースはシングルスレッドデータベースとして設計されているため、上記を防ぐことができますすべての問題
これらの4つの分離レベルにより、セキュリティが向上します。効率の低下

8.関数rand7()が1から7までのランダムな数を生成できることを知っているので、1から10までのランダムな数を生成できる関数を与えてください。このソリューションは、拒否サンプリングと呼ばれる方法に基づいています。主な考え方は、対象範囲内のランダム数が生成されている限り、直接返されるというものです。生成されたランダム数がターゲット範囲内にない場合は、値を破棄してリサンプリングします。対象範囲内の数値が同じ確率で選択されるため、このような均一な分布が生成されます。

明らかに、rand7は少なくとも2回実行する必要があります。そうしないと、1〜10の数字は生成されません。rand7を2回実行することにより、1〜49の整数を生成できます。
ここに写真の説明を挿入

49は10の倍数ではないため、いくつかの値を破棄する必要があります。必要な数値の範囲は1〜40です。この範囲にない場合は、破棄して再サンプリングします。

コード:

int rand10() {
    
    
  int row, col, idx;
  do {
    
    
    row = rand7();
    col = rand7();
    idx = col + (row-1)*7;
  } while (idx > 40);
  return 1 + (idx-1)%10;
}

行の範囲は1〜7、列の範囲は1〜7であるため、idx値の範囲は1〜49です。40を超える値は破棄されるため、1〜40の範囲の残りの数値はモジュロによって返されます。1〜40の範囲を満たすサンプリング回数の期待値を計算してみましょう。
ここに写真の説明を挿入

9.スレッドプールで利用できる最適化手段について説明してください。

スレッド待機時間の割合が高いほど、より多くのスレッドが必要になります。スレッドのCPU時間の割合が高いほど、必要なスレッドは少なくなります。
CPUを集中的に使用する操作の場合、スレッドの数はCPUコアの数と同じであるため、無駄なスイッチングスレッドのコンテキストが多く発生することを回避
できます。IOを集中的に使用し、多くの待機が必要な場合は、スレッドの数をさらに設定できます。 CPUコアに2を掛けたもの。

10.スレッドを作成するC ++ 11の3つの方法

  1. 関数
    スレッドを介して:標準ライブラリクラス
    join:メインスレッドをブロックして待機します
// MultiThread.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include<iostream>
#include<vector>
#include<map>
#include<string> 
#include<thread>

using namespace std;
void myPrint()
{
    
    
	cout << "线程开始运行" << endl;
	cout << "线程运行结束了" << endl;
}

int main()
{
    
    
	std::thread my2Obj(myPrint);  // 可调用对象
	my2Obj.join();// 主线程阻塞在这,并等待myPrint()执行完
	cout << "wangtao" << endl;
    return 0;
}

detach():メインスレッドと子スレッドを完全に分離します。子スレッドはバックグラウンドに常駐して実行され、C ++ランタイムライブラリに引き継がれ、制御を失います。

void myPrint()
{
    
    
	cout << "线程开始运行1" << endl;
	cout << "线程开始运行2" << endl;
	cout << "线程开始运行3" << endl;
	cout << "线程开始运行4" << endl;
	cout << "线程开始运行5" << endl;
	cout << "线程开始运行6" << endl;
	cout << "线程开始运行7" << endl;
	cout << "线程开始运行8" << endl;
	cout << "线程开始运行9" << endl;

}

int main()
{
    
    
	std::thread my2Obj(myPrint); // 主线程阻塞在这,并等待myPrint()执行完
	my2Obj.detach();
	cout << "wangtao1" << endl;
	cout << "wangtao2" << endl;
	cout << "wangtao3" << endl;
	cout << "wangtao4" << endl;
	cout << "wangtao5" << endl;
	cout << "wangtao6" << endl;
	cout << "wangtao7" << endl;
	cout << "wangtao8" << endl;
    return 0;
}

joinable():join()またはdetach()を正常に使用できるかどうかを判断します

プログラムの説明:デタッチ後に結合を実装することはできません

int main()
{
    
    
	std::thread my2Obj(myPrint); // 主线程阻塞在这,并等待myPrint()执行完
	if (my2Obj.joinable()){
    
    
		cout << "1:joinable() == true" << endl;
	}
	else {
    
    
		cout << "1:joinable() == false" << endl;
	}
	my2Obj.detach();

	if (my2Obj.joinable()) {
    
    
		cout << "2:joinable() == true" << endl;
	}
	else {
    
    
		cout << "2:joinable() == false" << endl;
	}
	cout << "wangtao1" << endl;
	cout << "wangtao2" << endl;
	cout << "wangtao3" << endl;
	cout << "wangtao4" << endl;
	cout << "wangtao5" << endl;
	cout << "wangtao6" << endl;
	cout << "wangtao7" << endl;
	cout << "wangtao8" << endl;
    return 0;
}

int main()
{
    
    
	std::thread my2Obj(myPrint); // 主线程阻塞在这,并等待myPrint()执行完
	if (my2Obj.joinable()){
    
    
		my2Obj.join();
	}
	cout << "wangtao1" << endl;
	cout << "wangtao2" << endl;
	cout << "wangtao3" << endl;
	cout << "wangtao4" << endl;
	cout << "wangtao5" << endl;
	cout << "wangtao6" << endl;
	cout << "wangtao7" << endl;
	cout << "wangtao8" << endl;
    return 0;
}

2.クラスオブジェクトを介してスレッドを作成します

class CObject
{
    
    
public:
	void operator ()() {
    
    
		cout << "线程开始运行" << endl;
		cout << "线程结束运行" << endl;
	}
};


int main()
{
    
    
	CObject obj;
	std::thread my2Obj(obj); // 主线程阻塞在这,并等待myPrint()执行完
	if (my2Obj.joinable()){
    
    
		my2Obj.join();
	}
	cout << "see you " << endl;
	
    return 0;
}


class CObject
{
    
    
	int& m_obj;
public:
	CObject(int& i) :m_obj(i) {
    
    }
	void operator ()() {
    
     // 不带参数
		cout << "线程开始运行1" << endl;
		cout << "线程开始运行2" << endl;
		cout << "线程开始运行3" << endl;
		cout << "线程开始运行4" << endl;
		cout << "线程开始运行5" << endl;
	}
};
int main()
{
    
    
	int i = 6;
	CObject obj(i);
	std::thread my2Obj(obj); // 主线程阻塞在这,并等待myPrint()执行完
	if (my2Obj.joinable()){
    
    
		my2Obj.detach();
	}
	cout << "see you " << endl;
	
    return 0;
}

メインスレッドがdetach()で終了すると、オブジェクトは破棄されます。子スレッドのメンバー関数を呼び出すことはできますか?
ここのオブジェクトは子スレッドにコピーされます。メインスレッドが終了しても、コピーされた子スレッドオブジェクトは破棄されません。
参照がない限り、ポインタに問題はありません。

コンストラクターとデストラクターをコピーして、オブジェクトが子スレッドにコピーされているかどうかを確認します

// MultiThread.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include<iostream>
#include<vector>
#include<map>
#include<string> 
#include<thread>
using namespace std;
class CObject
{
    
    
	int& m_obj;
public:
	CObject(int& i) :m_obj(i) {
    
    
		cout << "ctor" << endl;
	}
	CObject(const CObject& m) :m_obj(m.m_obj) {
    
    
		cout << "copy ctor" << endl;
	}
	~CObject(){
    
    
		cout << "dtor" << endl;
	}
	void operator ()() {
    
     // 不带参数
		cout << "线程开始运行1" << endl;
		cout << "线程开始运行2" << endl;
		cout << "线程开始运行3" << endl;
		cout << "线程开始运行4" << endl;
		cout << "线程开始运行5" << endl;
	}
};
int main()
{
    
    
	int i = 6;
	CObject obj(i);
	std::thread my2Obj(obj); // 主线程阻塞在这,并等待myPrint()执行完
	if (my2Obj.joinable()){
    
    
		my2Obj.detach();
	}
	cout << "see you " << endl;
	
    return 0;
}

子スレッドのデストラクタはバックグラウンドで実行されるため、出力dtorがメインスレッドになります。join()を使用した結果は次のとおりです。

3.ラムダ式を使用してスレッドを作成します

int main()
{
    
    
	auto myLamThread = [] {
    
    
		cout << "线程开始运行" << endl;
		cout << "线程结束运行" << endl;
	};
	thread cthread(myLamThread);
	cthread.join();
	std::cout << "see you " << endl;
	
    return 0;
}

あなたが学んだことを適用することはより良いです、そして上記に欠点があるかもしれません、議論を指摘することを歓迎します。

おすすめ

転載: blog.csdn.net/weixin_52622200/article/details/110440419